@@ -6,7 +6,10 @@ use foundry_compilers::{
66} ;
77use itertools:: Itertools ;
88use serde_json:: Value ;
9- use solar:: ast;
9+ use solar:: {
10+ ast:: { self , Span } ,
11+ interface:: Session ,
12+ } ;
1013use std:: { collections:: BTreeMap , path:: Path } ;
1114
1215/// Convenient struct to hold in-line per-test configurations
@@ -16,7 +19,7 @@ pub struct NatSpec {
1619 pub contract : String ,
1720 /// The function annotated with the natspec. None if the natspec is contract-level.
1821 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`.
2023 pub line : String ,
2124 /// The actual natspec comment, without slashes or block punctuation.
2225 pub docs : String ,
@@ -30,7 +33,8 @@ impl NatSpec {
3033 pub fn parse ( output : & ProjectCompileOutput , root : & Path ) -> Vec < Self > {
3134 let mut natspecs: Vec < Self > = vec ! [ ] ;
3235
33- let solar = SolarParser :: new ( ) ;
36+ let compiler = output. parser ( ) . solc ( ) . compiler ( ) ;
37+ let solar = SolarParser :: new ( compiler. sess ( ) ) ;
3438 let solc = SolcParser :: new ( ) ;
3539 for ( id, artifact) in output. artifact_ids ( ) {
3640 let path = id. source . as_path ( ) ;
@@ -41,7 +45,6 @@ impl NatSpec {
4145 let contract = format ! ( "{}:{}" , path. display( ) , id. name) ;
4246
4347 let mut used_solar = false ;
44- let compiler = output. parser ( ) . solc ( ) . compiler ( ) ;
4548 compiler. enter_sequential ( |compiler| {
4649 if let Some ( ( _, source) ) = compiler. gcx ( ) . get_ast_source ( abs_path)
4750 && let Some ( ast) = & source. ast
@@ -214,13 +217,13 @@ impl SolcParser {
214217 }
215218}
216219
217- struct SolarParser {
218- _private : ( ) ,
220+ struct SolarParser < ' a > {
221+ sess : & ' a Session ,
219222}
220223
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 }
224227 }
225228
226229 fn parse_ast (
@@ -234,6 +237,7 @@ impl SolarParser {
234237 if item. docs . is_empty ( ) {
235238 return ;
236239 }
240+ let mut span = Span :: DUMMY ;
237241 let lines = item
238242 . docs
239243 . iter ( )
@@ -242,6 +246,7 @@ impl SolarParser {
242246 if !s. contains ( INLINE_CONFIG_PREFIX ) {
243247 return None ;
244248 }
249+ span = if span. is_dummy ( ) { d. span } else { span. to ( d. span ) } ;
245250 match d. kind {
246251 ast:: CommentKind :: Line => Some ( s. trim ( ) . to_string ( ) ) ,
247252 ast:: CommentKind :: Block => Some (
@@ -257,8 +262,6 @@ impl SolarParser {
257262 if lines. is_empty ( ) {
258263 return ;
259264 }
260- let span =
261- item. docs . iter ( ) . map ( |doc| doc. span ) . reduce ( |a, b| a. to ( b) ) . unwrap_or_default ( ) ;
262265 natspecs. push ( NatSpec {
263266 contract : contract_id. to_string ( ) ,
264267 function : if let ast:: ItemKind :: Function ( f) = & item. kind {
@@ -271,7 +274,10 @@ impl SolarParser {
271274 } else {
272275 None
273276 } ,
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+ } ,
275281 docs : lines,
276282 } ) ;
277283 } ;
@@ -298,12 +304,10 @@ impl SolarParser {
298304mod tests {
299305 use super :: * ;
300306 use serde_json:: json;
307+ use snapbox:: { assert_data_eq, str} ;
301308 use solar:: parse:: {
302309 Parser ,
303- ast:: {
304- Arena ,
305- interface:: { self , Session } ,
306- } ,
310+ ast:: { Arena , interface} ,
307311 } ;
308312
309313 fn parse ( natspecs : & mut Vec < NatSpec > , src : & str , contract_id : & str , contract_name : & str ) {
@@ -315,6 +319,7 @@ mod tests {
315319 let sess = Session :: builder ( )
316320 . with_silent_emitter ( Some ( "Inline config parsing failed" . to_string ( ) ) )
317321 . build ( ) ;
322+ let solar = SolarParser :: new ( & sess) ;
318323 let _ = sess. enter ( || -> interface:: Result < ( ) > {
319324 let arena = Arena :: new ( ) ;
320325
@@ -327,7 +332,7 @@ mod tests {
327332
328333 let source_unit = parser. parse_file ( ) . map_err ( |e| e. emit ( ) ) ?;
329334
330- SolarParser :: new ( ) . parse_ast ( natspecs, & source_unit, contract_id, contract_name) ;
335+ solar . parse_ast ( natspecs, & source_unit, contract_id, contract_name) ;
331336
332337 Ok ( ( ) )
333338 } ) ;
@@ -388,40 +393,45 @@ function f2() {} /** forge-config: default.fuzz.runs = 800 */ function f3() {}
388393}
389394" ;
390395 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+ "# ] ]
425435 ) ;
426436 }
427437
@@ -444,18 +454,21 @@ contract FuzzInlineConf is DSTest {
444454}
445455 "# ;
446456 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+ "# ] ]
459472 ) ;
460473 }
461474
@@ -532,30 +545,44 @@ contract FuzzInlineConf2 is DSTest {
532545}
533546 "# ;
534547 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+ "# ] ]
545563 ) ;
546564
547565 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+ "# ] ]
559586 ) ;
560587 }
561588
@@ -575,24 +602,27 @@ contract FuzzInlineConf is DSTest {
575602 function testInlineConfFuzz2() {}
576603}"# ;
577604 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+ "# ] ]
596626 ) ;
597627 }
598628}
0 commit comments