8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use ast:: { Block , Crate , NodeId , Expr_ , ExprMac , Ident , mac_invoc_tt} ;
12
- use ast:: { item_mac, Stmt_ , StmtMac , StmtExpr , StmtSemi } ;
13
- use ast:: { ILLEGAL_CTXT } ;
11
+ use ast:: { Block , Crate , NodeId , DeclLocal , Expr_ , ExprMac , Local , Ident , mac_invoc_tt} ;
12
+ use ast:: { item_mac, Mrk , Stmt_ , StmtDecl , StmtMac , StmtExpr , StmtSemi } ;
13
+ use ast:: { ILLEGAL_CTXT , SCTable , token_tree } ;
14
14
use ast;
15
15
use ast_util:: { new_rename, new_mark, mtwt_resolve} ;
16
16
use attr;
@@ -23,7 +23,7 @@ use opt_vec;
23
23
use parse;
24
24
use parse:: { parse_item_from_source_str} ;
25
25
use parse:: token;
26
- use parse:: token:: { ident_to_str, intern} ;
26
+ use parse:: token:: { fresh_name , ident_to_str, intern} ;
27
27
use visit;
28
28
use visit:: Visitor ;
29
29
@@ -521,6 +521,71 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
521
521
522
522
}
523
523
524
+ // expand a non-macro stmt. this is essentially the fallthrough for
525
+ // expand_stmt, above.
526
+ fn expand_non_macro_stmt ( exts : SyntaxEnv ,
527
+ s : & Stmt_ ,
528
+ sp : Span ,
529
+ fld : @ast_fold ,
530
+ orig : @fn ( & Stmt_ , Span , @ast_fold ) -> ( Option < Stmt_ > , Span ) )
531
+ -> ( Option < Stmt_ > , Span ) {
532
+ // is it a let?
533
+ match * s {
534
+ StmtDecl ( @Spanned { node : DeclLocal ( ref local) , span : stmt_span} , node_id) => {
535
+ let block_info = get_block_info ( exts) ;
536
+ let pending_renames = block_info. pending_renames ;
537
+
538
+ // take it apart:
539
+ let @Local { is_mutbl : is_mutbl,
540
+ ty : _,
541
+ pat : pat,
542
+ init : init,
543
+ id : id,
544
+ span : span
545
+ } = * local;
546
+ // types can't be copied automatically because of the owned ptr in ty_tup...
547
+ let ty = local. ty . clone ( ) ;
548
+ // expand the pat (it might contain exprs... #:(o)>
549
+ let expanded_pat = fld. fold_pat ( pat) ;
550
+ // find the pat_idents in the pattern:
551
+ // oh dear heaven... this is going to include the enum names, as well....
552
+ let idents = @mut ~[ ] ;
553
+ let name_finder = new_name_finder ( idents) ;
554
+ name_finder. visit_pat ( expanded_pat, ( ) ) ;
555
+ // generate fresh names, push them to a new pending list
556
+ let new_pending_renames = @mut ~[ ] ;
557
+ for ident in idents. iter ( ) {
558
+ let new_name = fresh_name ( ident) ;
559
+ new_pending_renames. push ( ( * ident, new_name) ) ;
560
+ }
561
+ let mut rename_fld = renames_to_fold ( new_pending_renames) ;
562
+ // rewrite the pattern using the new names (the old ones
563
+ // have already been applied):
564
+ let rewritten_pat = rename_fld. fold_pat ( expanded_pat) ;
565
+ // add them to the existing pending renames:
566
+ for pr in new_pending_renames. iter ( ) { pending_renames. push ( * pr) }
567
+ // also, don't forget to expand the init:
568
+ let new_init_opt = init. map ( |e| fld. fold_expr ( * e) ) ;
569
+ let rewritten_local =
570
+ @Local { is_mutbl : is_mutbl,
571
+ ty : ty,
572
+ pat : rewritten_pat,
573
+ init : new_init_opt,
574
+ id : id,
575
+ span : span} ;
576
+ ( Some ( StmtDecl ( @Spanned { node : DeclLocal ( rewritten_local) ,
577
+ span : stmt_span} , node_id) ) ,
578
+ sp)
579
+ } ,
580
+ _ => {
581
+ orig ( s, sp, fld)
582
+ }
583
+ }
584
+ }
585
+
586
+ // a visitor that extracts the pat_ident paths
587
+ // from a given pattern and puts them in a mutable
588
+ // array (passed in to the traversal)
524
589
#[ deriving( Clone ) ]
525
590
struct NewNameFinderContext {
526
591
ident_accumulator : @mut ~[ ast:: Ident ] ,
@@ -674,30 +739,10 @@ pub fn new_name_finder(idents: @mut ~[ast::Ident]) -> @mut Visitor<()> {
674
739
context as @mut Visitor < ( ) >
675
740
}
676
741
677
- pub fn expand_block ( extsbox : @mut SyntaxEnv ,
678
- _cx : @ExtCtxt ,
679
- blk : & Block ,
680
- fld : @ast_fold ,
681
- orig : @fn ( & Block , @ast_fold ) -> Block )
682
- -> Block {
683
- // see note below about treatment of exts table
684
- with_exts_frame ! ( extsbox, false , orig( blk, fld) )
685
- }
686
-
687
-
688
- // get the (innermost) BlockInfo from an exts stack
689
- fn get_block_info ( exts : SyntaxEnv ) -> BlockInfo {
690
- match exts. find_in_topmost_frame ( & intern ( special_block_name) ) {
691
- Some ( @BlockInfo ( bi) ) => bi,
692
- _ => fail ! ( fmt!( "special identifier %? was bound to a non-BlockInfo" ,
693
- @" block" ) )
694
- }
695
- }
696
-
697
-
698
742
// given a mutable list of renames, return a tree-folder that applies those
699
743
// renames.
700
- fn renames_to_fold ( renames : @mut ~[ ( ast:: Ident , ast:: Name ) ] ) -> @ast_fold {
744
+ // FIXME #4536: currently pub to allow testing
745
+ pub fn renames_to_fold ( renames : @mut ~[ ( ast:: Ident , ast:: Name ) ] ) -> @ast_fold {
701
746
let afp = default_ast_fold ( ) ;
702
747
let f_pre = @AstFoldFns {
703
748
fold_ident : |id, _| {
@@ -713,15 +758,56 @@ fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold {
713
758
make_fold ( f_pre)
714
759
}
715
760
716
- // perform a bunch of renames
717
- fn apply_pending_renames ( folder : @ast_fold , stmt : ast:: Stmt ) -> @ast:: Stmt {
718
- match folder. fold_stmt ( & stmt) {
719
- Some ( s) => s,
720
- None => fail ! ( fmt!( "renaming of stmt produced None" ) )
761
+ pub fn expand_block ( extsbox : @mut SyntaxEnv ,
762
+ _cx : @ExtCtxt ,
763
+ blk : & Block ,
764
+ fld : @ast_fold ,
765
+ orig : @fn ( & Block , @ast_fold ) -> Block )
766
+ -> Block {
767
+ // see note below about treatment of exts table
768
+ with_exts_frame ! ( extsbox, false , orig( blk, fld) )
769
+ }
770
+
771
+
772
+ pub fn expand_block_elts ( exts : SyntaxEnv , b : & Block , fld : @ast_fold ) -> Block {
773
+ let block_info = get_block_info ( exts) ;
774
+ let pending_renames = block_info. pending_renames ;
775
+ let mut rename_fld = renames_to_fold ( pending_renames) ;
776
+ let new_view_items = b. view_items . map ( |x| fld. fold_view_item ( x) ) ;
777
+ let mut new_stmts = ~[ ] ;
778
+ for x in b. stmts . iter ( ) {
779
+ match fld. fold_stmt ( mustbesome ( rename_fld. fold_stmt ( * x) ) ) {
780
+ Some ( s) => new_stmts. push ( s) ,
781
+ None => ( )
782
+ }
783
+ }
784
+ let new_expr = b. expr . map ( |x| fld. fold_expr ( rename_fld. fold_expr ( * x) ) ) ;
785
+ Block {
786
+ view_items : new_view_items,
787
+ stmts : new_stmts,
788
+ expr : new_expr,
789
+ id : fld. new_id ( b. id ) ,
790
+ rules : b. rules ,
791
+ span : b. span ,
721
792
}
722
793
}
723
794
795
+ // rename_fold should never return "None".
796
+ fn mustbesome < T > ( val : Option < T > ) -> T {
797
+ match val {
798
+ Some ( v) => v,
799
+ None => fail ! ( "rename_fold returned None" )
800
+ }
801
+ }
724
802
803
+ // get the (innermost) BlockInfo from an exts stack
804
+ fn get_block_info ( exts : SyntaxEnv ) -> BlockInfo {
805
+ match exts. find_in_topmost_frame ( & intern ( special_block_name) ) {
806
+ Some ( @BlockInfo ( bi) ) => bi,
807
+ _ => fail ! ( fmt!( "special identifier %? was bound to a non-BlockInfo" ,
808
+ @" block" ) )
809
+ }
810
+ }
725
811
726
812
pub fn new_span ( cx : @ExtCtxt , sp : Span ) -> Span {
727
813
/* this discards information in the case of macro-defining macros */
@@ -1228,12 +1314,15 @@ mod test {
1228
1314
use super :: * ;
1229
1315
use ast;
1230
1316
use ast:: { Attribute_ , AttrOuter , MetaWord , EMPTY_CTXT } ;
1317
+ use ast_util:: { get_sctable, new_rename} ;
1231
1318
use codemap;
1232
1319
use codemap:: Spanned ;
1233
1320
use parse;
1234
- use parse:: token:: { intern, get_ident_interner} ;
1321
+ use parse:: token:: { gensym , intern, get_ident_interner} ;
1235
1322
use print:: pprust;
1236
- use util:: parser_testing:: { string_to_item, string_to_pat, strs_to_idents} ;
1323
+ use std;
1324
+ use util:: parser_testing:: { string_to_crate_and_sess, string_to_item, string_to_pat} ;
1325
+ use util:: parser_testing:: { strs_to_idents} ;
1237
1326
1238
1327
// make sure that fail! is present
1239
1328
#[ test] fn fail_exists_test ( ) {
@@ -1333,26 +1422,60 @@ mod test {
1333
1422
1334
1423
#[ test]
1335
1424
fn renaming ( ) {
1336
- let maybe_item_ast = string_to_item ( @"fn a ( ) -> int { let b = 13 ; b } ") ;
1337
- let item_ast = match maybe_item_ast {
1338
- Some ( x) => x,
1339
- None => fail ! ( "test case fail" )
1340
- } ;
1425
+ let item_ast = string_to_item ( @"fn a ( ) -> int { let b = 13 ; b } ") . unwrap ( ) ;
1341
1426
let a_name = intern ( "a" ) ;
1342
- let a2_name = intern ( "a2" ) ;
1427
+ let a2_name = gensym ( "a2" ) ;
1343
1428
let renamer = new_ident_renamer ( ast:: Ident { name : a_name, ctxt : EMPTY_CTXT } ,
1344
1429
a2_name) ;
1345
1430
let renamed_ast = fun_to_ident_folder ( renamer) . fold_item ( item_ast) . unwrap ( ) ;
1346
1431
let resolver = new_ident_resolver ( ) ;
1347
- let resolved_ast = fun_to_ident_folder ( resolver) . fold_item ( renamed_ast) . unwrap ( ) ;
1432
+ let resolver_fold = fun_to_ident_folder ( resolver) ;
1433
+ let resolved_ast = resolver_fold. fold_item ( renamed_ast) . unwrap ( ) ;
1348
1434
let resolved_as_str = pprust:: item_to_str ( resolved_ast,
1349
1435
get_ident_interner ( ) ) ;
1350
1436
assert_eq ! ( resolved_as_str, ~"fn a2( ) -> int { let b = 13 ; b } ");
1351
1437
1438
+ // try a double-rename, with pending_renames.
1439
+ let a3_name = gensym(" a3");
1440
+ let ctxt2 = new_rename(ast::Ident::new(a_name),a2_name,EMPTY_CTXT);
1441
+ let pending_renames = @mut ~[(ast::Ident::new(a_name),a2_name),
1442
+ (ast::Ident{name:a_name,ctxt:ctxt2},a3_name)];
1443
+ let double_renamed = renames_to_fold(pending_renames).fold_item(item_ast).unwrap();
1444
+ let resolved_again = resolver_fold.fold_item(double_renamed).unwrap();
1445
+ let double_renamed_as_str = pprust::item_to_str(resolved_again,
1446
+ get_ident_interner());
1447
+ assert_eq!(double_renamed_as_str,~" fn a3( ) -> int { let b = 13 ; b } ");
1448
+
1449
+ }
1352
1450
1451
+ fn fake_print_crate(s: @pprust::ps, crate: &ast::Crate) {
1452
+ pprust::print_mod(s, &crate.module, crate.attrs);
1353
1453
}
1354
1454
1355
- // sigh... it looks like I have two different renaming mechanisms, now...
1455
+ // " fn a( ) -> int { let b = 13 ; let c = b; b+c } " --> b & c should get new names, in the expr too.
1456
+ // " macro_rules! f ( ( $x: ident) => ( $x + b) ) fn a( ) -> int { let b = 13 ; f!( b) } " --> one should
1457
+ // be renamed, one should not.
1458
+
1459
+ fn expand_and_resolve_and_pretty_print (crate_str : @str) -> ~str {
1460
+ let resolver = new_ident_resolver();
1461
+ let resolver_fold = fun_to_ident_folder(resolver);
1462
+ let (crate_ast,ps) = string_to_crate_and_sess(crate_str);
1463
+ // the cfg argument actually does matter, here...
1464
+ let expanded_ast = expand_crate(ps,~[],crate_ast);
1465
+ // std::io::println(fmt!(" expanded: %?\n ", expanded_ast) ) ;
1466
+ let resolved_ast = resolver_fold. fold_crate ( expanded_ast) ;
1467
+ pprust:: to_str ( & resolved_ast, fake_print_crate, get_ident_interner ( ) )
1468
+ }
1469
+
1470
+ #[ test]
1471
+ fn automatic_renaming ( ) {
1472
+ let teststrs =
1473
+ ~[ @"fn a ( ) -> int { let b = 13 ; let c = b; b+c } ",
1474
+ @" macro_rules! f ( ( $x: ident) => ( $x + b) ) fn a ( ) -> int { let b = 13 ; f ! ( b) } "];
1475
+ for s in teststrs.iter() {
1476
+ std::io::println(expand_and_resolve_and_pretty_print(*s));
1477
+ }
1478
+ }
1356
1479
1357
1480
#[test]
1358
1481
fn pat_idents(){
0 commit comments