fork of https://github.com/tree-sitter/tree-sitter-graph
at main 22 kB view raw
1// -*- coding: utf-8 -*- 2// ------------------------------------------------------------------------------------------------ 3// Copyright © 2021, tree-sitter authors. 4// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. 5// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. 6// ------------------------------------------------------------------------------------------------ 7 8use indoc::indoc; 9use tree_sitter::Parser; 10use tree_sitter_graph::ast::File; 11use tree_sitter_graph::functions::Functions; 12use tree_sitter_graph::ExecutionConfig; 13use tree_sitter_graph::ExecutionError; 14use tree_sitter_graph::Identifier; 15use tree_sitter_graph::NoCancellation; 16use tree_sitter_graph::Variables; 17 18fn init_log() { 19 let _ = env_logger::builder() 20 .is_test(true) 21 .format_level(false) 22 .format_target(false) 23 .format_timestamp(None) 24 .try_init(); // try, because earlier test may have already initialized it 25} 26 27fn execute(python_source: &str, dsl_source: &str) -> Result<String, ExecutionError> { 28 init_log(); 29 let mut parser = Parser::new(); 30 parser 31 .set_language(&tree_sitter_python::LANGUAGE.into()) 32 .unwrap(); 33 let tree = parser.parse(python_source, None).unwrap(); 34 let file = 35 File::from_str(tree_sitter_python::LANGUAGE.into(), dsl_source).expect("Cannot parse file"); 36 let functions = Functions::stdlib(); 37 let mut globals = Variables::new(); 38 globals 39 .add(Identifier::from("filename"), "test.py".into()) 40 .map_err(|_| ExecutionError::DuplicateVariable("filename".into()))?; 41 let mut config = ExecutionConfig::new(&functions, &globals); 42 let graph = file.execute(&tree, python_source, &mut config, &NoCancellation)?; 43 let result = graph.pretty_print().to_string(); 44 Ok(result) 45} 46 47fn check_execution(python_source: &str, dsl_source: &str, expected_graph: &str) { 48 match execute(python_source, dsl_source) { 49 Ok(actual_graph) => assert_eq!(actual_graph, expected_graph), 50 Err(e) => panic!("Could not execute file: {}", e), 51 } 52} 53 54fn fail_execution(python_source: &str, dsl_source: &str) { 55 if let Ok(_) = execute(python_source, dsl_source) { 56 panic!("Execution succeeded unexpectedly"); 57 } 58} 59 60#[test] 61fn can_build_simple_graph() { 62 check_execution( 63 "pass", 64 indoc! {r#" 65 (module) @root 66 { 67 node node0 68 attr (node0) name = "node0", source = @root 69 var node1 = (node) 70 attr (node1) name = "node1" 71 edge node0 -> node1 72 attr (node0 -> node1) precedence = 14 73 node node2 74 attr (node2) name = "node2", parent = node1 75 } 76 "#}, 77 indoc! {r#" 78 node 0 79 name: "node0" 80 source: [syntax node module (1, 1)] 81 edge 0 -> 1 82 precedence: 14 83 node 1 84 name: "node1" 85 node 2 86 name: "node2" 87 parent: [graph node 1] 88 "#}, 89 ); 90} 91 92#[test] 93fn can_scan_strings() { 94 check_execution( 95 "pass", 96 indoc! {r#" 97 (module) 98 { 99 var new_node = #null 100 var current_node = (node) 101 102 scan "alpha/beta/gamma/delta.py" { 103 "([^/]+)/" 104 { 105 set new_node = (node) 106 attr (new_node) name = $1 107 edge current_node -> new_node 108 set current_node = new_node 109 } 110 111 "([^/]+)\\.py$" 112 { 113 set new_node = (node) 114 attr (new_node) name = $1 115 edge current_node -> new_node 116 } 117 } 118 } 119 "#}, 120 indoc! {r#" 121 node 0 122 edge 0 -> 1 123 node 1 124 name: "alpha" 125 edge 1 -> 2 126 node 2 127 name: "beta" 128 edge 2 -> 3 129 node 3 130 name: "gamma" 131 edge 3 -> 4 132 node 4 133 name: "delta" 134 "#}, 135 ); 136} 137 138#[test] 139fn variables_in_scan_arms_are_local() { 140 check_execution( 141 "pass", 142 indoc! {r#" 143 (module) 144 { 145 var current_node = (node) 146 147 scan "alpha/beta/gamma/delta.py" { 148 "([^/]+)/" 149 { 150 let new_node = (node) 151 attr (new_node) name = $1 152 edge current_node -> new_node 153 set current_node = new_node 154 } 155 156 "([^/]+)\\.py$" 157 { 158 let new_node = (node) 159 attr (new_node) name = $1 160 edge current_node -> new_node 161 } 162 } 163 } 164 "#}, 165 indoc! {r#" 166 node 0 167 edge 0 -> 1 168 node 1 169 name: "alpha" 170 edge 1 -> 2 171 node 2 172 name: "beta" 173 edge 2 -> 3 174 node 3 175 name: "gamma" 176 edge 3 -> 4 177 node 4 178 name: "delta" 179 "#}, 180 ); 181} 182 183#[test] 184fn scoped_variables_carry_across_stanzas() { 185 check_execution( 186 indoc! {r#" 187 import a 188 from b import c 189 print(a.d.f) 190 "#}, 191 indoc! {r#" 192 (identifier) @id 193 { 194 let @id.node = (node) 195 } 196 197 (identifier) @id 198 { 199 attr (@id.node) name = (source-text @id) 200 } 201 "#}, 202 indoc! {r#" 203 node 0 204 name: "a" 205 node 1 206 name: "b" 207 node 2 208 name: "c" 209 node 3 210 name: "print" 211 node 4 212 name: "a" 213 node 5 214 name: "d" 215 node 6 216 name: "f" 217 "#}, 218 ); 219} 220 221#[test] 222fn can_match_stanza_multiple_times() { 223 check_execution( 224 indoc! {r#" 225 import a 226 from b import c 227 print(a.d.f) 228 "#}, 229 indoc! {r#" 230 (identifier) @id 231 { 232 node new_node 233 attr (new_node) name = (source-text @id) 234 } 235 "#}, 236 indoc! {r#" 237 node 0 238 name: "a" 239 node 1 240 name: "b" 241 node 2 242 name: "c" 243 node 3 244 name: "print" 245 node 4 246 name: "a" 247 node 5 248 name: "d" 249 node 6 250 name: "f" 251 "#}, 252 ); 253} 254 255#[test] 256fn can_use_global_variable() { 257 check_execution( 258 "pass", 259 indoc! {r#" 260 global filename 261 262 (module) 263 { 264 node n 265 attr (n) filename = filename 266 } 267 "#}, 268 indoc! {r#" 269 node 0 270 filename: "test.py" 271 "#}, 272 ); 273} 274 275#[test] 276fn can_omit_global_variable_with_default() { 277 check_execution( 278 "pass", 279 indoc! {r#" 280 global pkgname = "" 281 282 (module) 283 { 284 node n 285 attr (n) pkgname = pkgname 286 } 287 "#}, 288 indoc! {r#" 289 node 0 290 pkgname: "" 291 "#}, 292 ); 293} 294 295#[test] 296fn cannot_omit_global_variable() { 297 fail_execution( 298 "pass", 299 indoc! {r#" 300 global root 301 302 (identifier) { 303 node n 304 edge n -> root 305 } 306 "#}, 307 ); 308} 309 310#[test] 311fn cannot_pass_string_to_global_list_variable() { 312 fail_execution( 313 "pass", 314 indoc! {r#" 315 global filename* 316 "#}, 317 ); 318} 319 320#[test] 321fn can_use_variable_multiple_times() { 322 check_execution( 323 "pass", 324 indoc! {r#" 325 (module) 326 { 327 let x = (node) 328 let y = x 329 let z = x 330 } 331 "#}, 332 indoc! {r#" 333 node 0 334 "#}, 335 ); 336} 337 338#[test] 339fn can_nest_function_calls() { 340 check_execution( 341 "pass", 342 indoc! {r#" 343 (module) 344 { 345 node node0 346 attr (node0) val = (replace "accacc" (replace "abc" "b" "c") (replace "abc" "a" "b")) 347 } 348 "#}, 349 indoc! {r#" 350 node 0 351 val: "bbcbbc" 352 "#}, 353 ); 354} 355 356#[test] 357fn cannot_use_nullable_regex() { 358 fail_execution( 359 "pass", 360 indoc! {r#" 361 (module) 362 { 363 scan "abc" { 364 "^\\b" { 365 } 366 } 367 node n 368 } 369 "#}, 370 ); 371} 372 373#[test] 374fn can_create_present_optional_capture() { 375 check_execution( 376 "pass", 377 indoc! {r#" 378 (module (_)? @stmts) 379 { 380 node n 381 attr (n) stmts = @stmts 382 } 383 "#}, 384 indoc! {r#" 385 node 0 386 stmts: [syntax node pass_statement (1, 1)] 387 "#}, 388 ); 389} 390 391#[test] 392fn can_create_missing_optional_capture() { 393 check_execution( 394 indoc! {r#" 395 "#}, 396 indoc! {r#" 397 (module (_)? @stmts) 398 { 399 node n 400 attr (n) stmts = @stmts 401 } 402 "#}, 403 indoc! {r#" 404 node 0 405 stmts: #null 406 "#}, 407 ); 408} 409 410#[test] 411fn can_create_empty_list_capture() { 412 check_execution( 413 indoc! {r#" 414 "#}, 415 indoc! {r#" 416 (module (_)* @stmts) 417 { 418 node n 419 attr (n) stmts = @stmts 420 } 421 "#}, 422 indoc! {r#" 423 node 0 424 stmts: [] 425 "#}, 426 ); 427} 428 429#[test] 430fn can_create_nonempty_list_capture() { 431 check_execution( 432 indoc! {r#" 433 pass 434 pass 435 "#}, 436 indoc! {r#" 437 (module (_)+ @stmts) 438 { 439 node n 440 attr (n) stmts = @stmts 441 } 442 "#}, 443 indoc! {r#" 444 node 0 445 stmts: [[syntax node pass_statement (1, 1)], [syntax node pass_statement (2, 1)]] 446 "#}, 447 ); 448} 449 450#[test] 451fn can_execute_if_some() { 452 check_execution( 453 "pass", 454 indoc! {r#" 455 (module (pass_statement)? @x) 456 { 457 node node0 458 if some @x { 459 attr (node0) val = 0 460 } else { 461 attr (node0) val = 1 462 } 463 } 464 "#}, 465 indoc! {r#" 466 node 0 467 val: 0 468 "#}, 469 ); 470} 471 472#[test] 473fn can_execute_if_none() { 474 check_execution( 475 "pass", 476 indoc! {r#" 477 (module (import_statement)? @x) 478 { 479 node node0 480 if none @x { 481 attr (node0) val = 0 482 } else { 483 attr (node0) val = 1 484 } 485 } 486 "#}, 487 indoc! {r#" 488 node 0 489 val: 0 490 "#}, 491 ); 492} 493 494#[test] 495fn can_execute_if_some_and_none() { 496 check_execution( 497 "pass", 498 indoc! {r#" 499 (module (import_statement)? @x (pass_statement)? @y) 500 { 501 node node0 502 if none @x, some @y { 503 attr (node0) val = 1 504 } elif some @y { 505 attr (node0) val = 0 506 } 507 } 508 "#}, 509 indoc! {r#" 510 node 0 511 val: 1 512 "#}, 513 ); 514} 515 516#[test] 517fn can_execute_elif() { 518 check_execution( 519 "pass", 520 indoc! {r#" 521 (module (import_statement)? @x (pass_statement)? @y) 522 { 523 node node0 524 if some @x { 525 attr (node0) val = 0 526 } elif some @y { 527 attr (node0) val = 1 528 } 529 } 530 "#}, 531 indoc! {r#" 532 node 0 533 val: 1 534 "#}, 535 ); 536} 537 538#[test] 539fn can_execute_else() { 540 check_execution( 541 "pass", 542 indoc! {r#" 543 (module (import_statement)? @x) 544 { 545 node node0 546 if some @x { 547 attr (node0) val = 0 548 } else { 549 attr (node0) val = 1 550 } 551 } 552 "#}, 553 indoc! {r#" 554 node 0 555 val: 1 556 "#}, 557 ); 558} 559 560#[test] 561fn can_execute_if_literal() { 562 check_execution( 563 "pass", 564 indoc! {r#" 565 (module (import_statement)?) 566 { 567 node node0 568 if #true { 569 attr (node0) val = 0 570 } else { 571 attr (node0) val = 1 572 } 573 } 574 "#}, 575 indoc! {r#" 576 node 0 577 val: 0 578 "#}, 579 ); 580} 581 582#[test] 583fn skip_if_without_true_conditions() { 584 check_execution( 585 "pass", 586 indoc! {r#" 587 (module (import_statement)? @x (import_statement)? @y) 588 { 589 node node0 590 if some @x { 591 attr (node0) val = 0 592 } elif some @y { 593 attr (node0) val = 1 594 } 595 } 596 "#}, 597 indoc! {r#" 598 node 0 599 "#}, 600 ); 601} 602 603#[test] 604fn variables_are_local_in_if_body() { 605 check_execution( 606 r#" 607 pass 608 "#, 609 indoc! {r#" 610 (module (pass_statement)? @x) 611 { 612 let n = 1 613 if some @x { 614 let n = 2 615 } 616 node node0 617 attr (node0) val = n 618 } 619 "#}, 620 indoc! {r#" 621 node 0 622 val: 1 623 "#}, 624 ); 625} 626 627#[test] 628fn variables_do_not_escape_if_body() { 629 check_execution( 630 r#" 631 pass 632 "#, 633 indoc! {r#" 634 (module (pass_statement)? @x) 635 { 636 var n = 1 637 if some @x { 638 var n = 2 639 } 640 node node0 641 attr (node0) val = n 642 } 643 "#}, 644 indoc! {r#" 645 node 0 646 val: 1 647 "#}, 648 ); 649} 650 651#[test] 652fn variables_are_inherited_in_if_body() { 653 check_execution( 654 r#" 655 pass 656 "#, 657 indoc! {r#" 658 (module (pass_statement)? @x) 659 { 660 var n = 1 661 if some @x { 662 set n = (plus n 1) 663 } 664 node node0 665 attr (node0) val = n 666 } 667 "#}, 668 indoc! {r#" 669 node 0 670 val: 2 671 "#}, 672 ); 673} 674 675#[test] 676fn can_execute_for_in_nonempty_list_capture() { 677 check_execution( 678 r#" 679 pass 680 pass 681 pass 682 "#, 683 indoc! {r#" 684 (module (pass_statement)* @xs) 685 { 686 var n = 0 687 for x in @xs { 688 set n = (plus n 1) 689 } 690 node node0 691 attr (node0) val = n 692 } 693 "#}, 694 indoc! {r#" 695 node 0 696 val: 3 697 "#}, 698 ); 699} 700 701#[test] 702fn can_execute_for_in_empty_list_capture() { 703 check_execution( 704 r#" 705 pass 706 "#, 707 indoc! {r#" 708 (module (import_statement)* @xs) 709 { 710 var n = 0 711 for x in @xs { 712 set n = (plus n 1) 713 } 714 node node0 715 attr (node0) val = n 716 } 717 "#}, 718 indoc! {r#" 719 node 0 720 val: 0 721 "#}, 722 ); 723} 724 725#[test] 726fn can_execute_for_in_list_literal() { 727 check_execution( 728 r#" 729 pass 730 "#, 731 indoc! {r#" 732 (module) 733 { 734 var n = 0 735 for x in [#null, #null, #null] { 736 set n = (plus n 1) 737 } 738 node node0 739 attr (node0) val = n 740 } 741 "#}, 742 indoc! {r#" 743 node 0 744 val: 3 745 "#}, 746 ); 747} 748 749#[test] 750fn variables_are_local_in_for_in_body() { 751 check_execution( 752 r#" 753 pass 754 "#, 755 indoc! {r#" 756 (module (pass_statement)* @xs) 757 { 758 let n = 1 759 for x in @xs { 760 let n = 2 761 } 762 node node0 763 attr (node0) val = n 764 } 765 "#}, 766 indoc! {r#" 767 node 0 768 val: 1 769 "#}, 770 ); 771} 772 773#[test] 774fn variables_do_not_escape_for_in_body() { 775 check_execution( 776 r#" 777 pass 778 "#, 779 indoc! {r#" 780 (module (pass_statement)* @xs) 781 { 782 var n = 1 783 for x in @xs { 784 var n = 2 785 } 786 node node0 787 attr (node0) val = n 788 } 789 "#}, 790 indoc! {r#" 791 node 0 792 val: 1 793 "#}, 794 ); 795} 796 797#[test] 798fn variables_are_inherited_in_for_in_body() { 799 check_execution( 800 r#" 801 pass 802 pass 803 pass 804 "#, 805 indoc! {r#" 806 (module (pass_statement)+ @xs) 807 { 808 var n = 0 809 for x in @xs { 810 set n = (plus n 1) 811 } 812 node node0 813 attr (node0) val = n 814 } 815 "#}, 816 indoc! {r#" 817 node 0 818 val: 3 819 "#}, 820 ); 821} 822 823#[test] 824fn can_execute_list_comprehension() { 825 check_execution( 826 r#" 827 pass 828 pass 829 pass 830 "#, 831 indoc! {r#" 832 (module (pass_statement)* @xs) 833 { 834 node node0 835 attr (node0) val = [ (named-child-index x) for x in @xs ] 836 } 837 "#}, 838 indoc! {r#" 839 node 0 840 val: [0, 1, 2] 841 "#}, 842 ); 843} 844 845#[test] 846fn can_execute_set_comprehension() { 847 check_execution( 848 r#" 849 pass 850 pass 851 pass 852 "#, 853 indoc! {r#" 854 (module (pass_statement)* @xs) 855 { 856 node node0 857 attr (node0) val = { (source-text x) for x in @xs } 858 } 859 "#}, 860 indoc! {r#" 861 node 0 862 val: {"pass"} 863 "#}, 864 ); 865} 866 867#[test] 868fn can_execute_scan_of_local_call_expression() { 869 check_execution( 870 r#" 871 def get_f(): 872 pass 873 "#, 874 indoc! {r#" 875 (function_definition 876 name: (identifier) @name) 877 { 878 node n 879 scan (source-text @name) { 880 "get_.*" { 881 attr (n) is_getter = #true 882 } 883 } 884 } 885 "#}, 886 indoc! {r#" 887 node 0 888 is_getter: #true 889 "#}, 890 ); 891} 892 893#[test] 894fn can_execute_scan_of_local_variable() { 895 check_execution( 896 r#" 897 def get_f(): 898 pass 899 "#, 900 indoc! {r#" 901 (function_definition 902 name: (identifier) @name) 903 { 904 node n 905 let val = (source-text @name) 906 scan val { 907 "get_.*" { 908 attr (n) is_getter = #true 909 } 910 } 911 } 912 "#}, 913 indoc! {r#" 914 node 0 915 is_getter: #true 916 "#}, 917 ); 918} 919 920#[test] 921fn can_execute_shorthand() { 922 check_execution( 923 indoc! { r#" 924 def get_f(): 925 pass 926 "#}, 927 indoc! {r#" 928 attribute def = x => source_node = x, symbol = (source-text x) 929 (function_definition name: (identifier) @name) { 930 node n 931 attr (n) def = @name 932 } 933 "#}, 934 indoc! {r#" 935 node 0 936 source_node: [syntax node identifier (1, 5)] 937 symbol: "get_f" 938 "#}, 939 ); 940} 941 942#[test] 943fn can_access_inherited_attribute() { 944 check_execution( 945 indoc! { r#" 946 def get_f(): 947 pass 948 "#}, 949 indoc! {r#" 950 inherit .test 951 (function_definition)@def { 952 node @def.test 953 attr (@def.test) in_def 954 } 955 (pass_statement)@pass { 956 attr (@pass.test) in_pass 957 } 958 "#}, 959 indoc! {r#" 960 node 0 961 in_def: #true 962 in_pass: #true 963 "#}, 964 ); 965} 966 967#[test] 968fn can_overwrite_inherited_attribute() { 969 check_execution( 970 indoc! { r#" 971 def get_f(): 972 pass 973 "#}, 974 indoc! {r#" 975 inherit .test 976 (function_definition)@def { 977 node @def.test 978 attr (@def.test) in_def 979 } 980 (pass_statement)@pass { 981 node @pass.test 982 } 983 (pass_statement)@pass { 984 attr (@pass.test) in_pass 985 } 986 "#}, 987 indoc! {r#" 988 node 0 989 in_def: #true 990 node 1 991 in_pass: #true 992 "#}, 993 ); 994} 995 996#[test] 997fn cannot_access_non_inherited_variable() { 998 fail_execution( 999 indoc! { r#" 1000 def get_f(): 1001 pass 1002 "#}, 1003 indoc! {r#" 1004 (function_definition)@def { 1005 node @def.test 1006 } 1007 (pass_statement)@pass { 1008 attr (@pass.test) in_pass 1009 } 1010 "#}, 1011 ); 1012} 1013 1014#[test] 1015fn can_add_edge_twice() { 1016 check_execution( 1017 indoc! { r#" 1018 pass 1019 "#}, 1020 indoc! {r#" 1021 (module) { 1022 node n1; 1023 node n2; 1024 edge n1 -> n2; 1025 edge n1 -> n2; 1026 } 1027 "#}, 1028 indoc! {r#" 1029 node 0 1030 edge 0 -> 1 1031 node 1 1032 "#}, 1033 ); 1034} 1035 1036#[test] 1037fn can_set_node_attribute_value_twice() { 1038 check_execution( 1039 indoc! { r#" 1040 pass 1041 "#}, 1042 indoc! {r#" 1043 (module) { 1044 node n; 1045 attr (n) foo = #true; 1046 } 1047 "#}, 1048 indoc! {r#" 1049 node 0 1050 foo: #true 1051 "#}, 1052 ); 1053} 1054 1055#[test] 1056fn cannot_change_attribute_value() { 1057 check_execution( 1058 indoc! { r#" 1059 pass 1060 "#}, 1061 indoc! {r#" 1062 (module) { 1063 node n1; 1064 node n2; 1065 edge n1 -> n2; 1066 attr (n1 -> n2) foo = #true; 1067 } 1068 "#}, 1069 indoc! {r#" 1070 node 0 1071 edge 0 -> 1 1072 foo: #true 1073 node 1 1074 "#}, 1075 ); 1076}