fork of https://github.com/tree-sitter/tree-sitter-graph

Make unused query captures errors

Changed files
+194 -106
src
reference
tests
+91 -26
src/checker.rs
··· 46 46 UndefinedSyntaxCapture(String, Location), 47 47 #[error("Undefined variable {0} at {1}")] 48 48 UndefinedVariable(String, Location), 49 + #[error("Unused capture(s) {0} at {1}. Remove or prefix with _.")] 50 + UnusedCaptures(String, Location), 49 51 #[error("{0}: {1} at {2}")] 50 52 Variable(VariableError, String, Location), 51 53 } ··· 82 84 CheckError::NullableRegex(_, location) => *location, 83 85 CheckError::UndefinedSyntaxCapture(_, location) => *location, 84 86 CheckError::UndefinedVariable(_, location) => *location, 87 + CheckError::UnusedCaptures(_, location) => *location, 85 88 CheckError::Variable(_, _, location) => *location, 86 89 }; 87 90 writeln!(f, "{}", self.error)?; ··· 163 166 ctx.file_query 164 167 .capture_index_for_name(FULL_MATCH) 165 168 .expect("missing capture index for full match") as usize; 169 + 170 + let mut used_captures = HashSet::new(); 166 171 for statement in &mut self.statements { 167 - statement.check(&mut ctx)?; 172 + let stmt_result = statement.check(&mut ctx)?; 173 + used_captures.extend(stmt_result.used_captures); 174 + } 175 + 176 + let all_captures = self 177 + .query 178 + .capture_names() 179 + .into_iter() 180 + .filter(|cn| { 181 + self.query 182 + .capture_index_for_name(cn) 183 + .expect("capture should have index") 184 + != self.full_match_stanza_capture_index as u32 185 + }) 186 + .map(|cn| Identifier::from(cn.as_str())) 187 + .collect::<HashSet<_>>(); 188 + let unused_captures = all_captures 189 + .difference(&used_captures) 190 + .filter(|i| !i.starts_with("_")) 191 + .map(|i| format!("@{}", i)) 192 + .collect::<Vec<_>>(); 193 + if !unused_captures.is_empty() { 194 + return Err(CheckError::UnusedCaptures( 195 + unused_captures.join(" "), 196 + self.location, 197 + )); 168 198 } 199 + 169 200 Ok(()) 170 201 } 171 202 } ··· 198 229 199 230 impl ast::DeclareImmutable { 200 231 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 232 + let mut used_captures = HashSet::new(); 201 233 let value = self.value.check(ctx)?; 202 - let used_captures = value.used_captures.clone(); 203 - self.variable.check_add(ctx, value, false)?; 234 + used_captures.extend(value.used_captures.iter().cloned()); 235 + let var_result = self.variable.check_add(ctx, value, false)?; 236 + used_captures.extend(var_result.used_captures); 204 237 Ok(StatementResult { used_captures }) 205 238 } 206 239 } 207 240 208 241 impl ast::DeclareMutable { 209 242 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 243 + let mut used_captures = HashSet::new(); 210 244 let value = self.value.check(ctx)?; 211 - let used_captures = value.used_captures.clone(); 212 - self.variable.check_add(ctx, value, true)?; 245 + used_captures.extend(value.used_captures.iter().cloned()); 246 + let var_result = self.variable.check_add(ctx, value, true)?; 247 + used_captures.extend(var_result.used_captures); 213 248 Ok(StatementResult { used_captures }) 214 249 } 215 250 } 216 251 217 252 impl ast::Assign { 218 253 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 254 + let mut used_captures = HashSet::new(); 219 255 let value = self.value.check(ctx)?; 220 - let used_captures = value.used_captures.clone(); 221 - self.variable.check_set(ctx, value)?; 256 + used_captures.extend(value.used_captures.iter().cloned()); 257 + let var_result = self.variable.check_set(ctx, value)?; 258 + used_captures.extend(var_result.used_captures); 222 259 Ok(StatementResult { used_captures }) 223 260 } 224 261 } 225 262 226 263 impl ast::CreateGraphNode { 227 264 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 228 - self.node.check_add( 265 + let node_result = self.node.check_add( 229 266 ctx, 230 267 ExpressionResult { 231 268 is_local: true, ··· 235 272 false, 236 273 )?; 237 274 Ok(StatementResult { 238 - used_captures: HashSet::default(), 275 + used_captures: node_result.used_captures, 239 276 }) 240 277 } 241 278 } ··· 406 443 stanza_query: ctx.stanza_query, 407 444 locals: &mut loop_locals, 408 445 }; 409 - self.variable 446 + let var_result = self 447 + .variable 410 448 .check_add(&mut loop_ctx, value_result, false)?; 449 + used_captures.extend(var_result.used_captures); 450 + 411 451 for statement in &mut self.statements { 412 452 let stmt_result = statement.check(&mut loop_ctx)?; 413 453 used_captures.extend(stmt_result.used_captures); ··· 535 575 stanza_query: ctx.stanza_query, 536 576 locals: &mut loop_locals, 537 577 }; 538 - self.variable 578 + let var_result = self 579 + .variable 539 580 .check_add(&mut loop_ctx, value_result, false)?; 581 + used_captures.extend(var_result.used_captures); 540 582 541 583 let element_result = self.element.check(&mut loop_ctx)?; 542 584 used_captures.extend(element_result.used_captures); ··· 570 612 stanza_query: ctx.stanza_query, 571 613 locals: &mut loop_locals, 572 614 }; 573 - self.variable 615 + let var_result = self 616 + .variable 574 617 .check_add(&mut loop_ctx, value_result, false)?; 618 + used_captures.extend(var_result.used_captures); 575 619 576 620 let element_result = self.element.check(&mut loop_ctx)?; 577 621 used_captures.extend(element_result.used_captures); ··· 636 680 //----------------------------------------------------------------------------- 637 681 // Variables 638 682 683 + #[derive(Clone, Debug)] 684 + struct VariableResult { 685 + used_captures: HashSet<Identifier>, 686 + } 687 + 639 688 impl ast::Variable { 640 689 fn check_add( 641 690 &mut self, 642 691 ctx: &mut CheckContext, 643 692 value: ExpressionResult, 644 693 mutable: bool, 645 - ) -> Result<(), CheckError> { 694 + ) -> Result<VariableResult, CheckError> { 646 695 match self { 647 696 Self::Unscoped(v) => v.check_add(ctx, value, mutable), 648 697 Self::Scoped(v) => v.check_add(ctx, value, mutable), ··· 653 702 &mut self, 654 703 ctx: &mut CheckContext, 655 704 value: ExpressionResult, 656 - ) -> Result<(), CheckError> { 705 + ) -> Result<VariableResult, CheckError> { 657 706 match self { 658 707 Self::Unscoped(v) => v.check_set(ctx, value), 659 708 Self::Scoped(v) => v.check_set(ctx, value), ··· 674 723 ctx: &mut CheckContext, 675 724 value: ExpressionResult, 676 725 mutable: bool, 677 - ) -> Result<(), CheckError> { 726 + ) -> Result<VariableResult, CheckError> { 678 727 if ctx.globals.get(&self.name).is_some() { 679 728 return Err(CheckError::CannotHideGlobalVariable( 680 729 self.name.as_str().to_string(), ··· 689 738 if mutable { 690 739 value.is_local = false; 691 740 } 741 + let used_captures = value.used_captures.clone(); 742 + value.used_captures.clear(); // prevent used captures from escaping 743 + // we may want to separate quantifier/is_local from 744 + // the used_captures and only store the former in the 745 + // future for a cleaner solution 692 746 ctx.locals 693 747 .add(self.name.clone(), value, mutable) 694 - .map_err(|e| CheckError::Variable(e, format!("{}", self.name), self.location)) 748 + .map_err(|e| CheckError::Variable(e, format!("{}", self.name), self.location))?; 749 + Ok(VariableResult { used_captures }) 695 750 } 696 751 697 752 fn check_set( 698 753 &mut self, 699 754 ctx: &mut CheckContext, 700 755 value: ExpressionResult, 701 - ) -> Result<(), CheckError> { 756 + ) -> Result<VariableResult, CheckError> { 702 757 if ctx.globals.get(&self.name).is_some() { 703 758 return Err(CheckError::CannotSetGlobalVariable( 704 759 self.name.as_str().to_string(), ··· 711 766 // Since we process all statement in order, we don't have info on later 712 767 // assignments, and can assume non-local to be sound. 713 768 value.is_local = false; 769 + let used_captures = value.used_captures.clone(); 770 + value.used_captures.clear(); // prevent used captures from escaping 771 + // we may want to separate quantifier/is_local from 772 + // the used_captures and only store the former in the 773 + // future for a cleaner solution 714 774 ctx.locals 715 775 .set(self.name.clone(), value) 716 - .map_err(|e| CheckError::Variable(e, format!("{}", self.name), self.location)) 776 + .map_err(|e| CheckError::Variable(e, format!("{}", self.name), self.location))?; 777 + Ok(VariableResult { used_captures }) 717 778 } 718 779 719 780 fn check_get(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { ··· 733 794 ctx: &mut CheckContext, 734 795 _value: ExpressionResult, 735 796 _mutable: bool, 736 - ) -> Result<(), CheckError> { 737 - self.scope.check(ctx)?; 738 - Ok(()) 797 + ) -> Result<VariableResult, CheckError> { 798 + let scope_result = self.scope.check(ctx)?; 799 + Ok(VariableResult { 800 + used_captures: scope_result.used_captures, 801 + }) 739 802 } 740 803 741 804 fn check_set( 742 805 &mut self, 743 806 ctx: &mut CheckContext, 744 807 _value: ExpressionResult, 745 - ) -> Result<(), CheckError> { 746 - self.scope.check(ctx)?; 747 - Ok(()) 808 + ) -> Result<VariableResult, CheckError> { 809 + let scope_result = self.scope.check(ctx)?; 810 + Ok(VariableResult { 811 + used_captures: scope_result.used_captures, 812 + }) 748 813 } 749 814 750 815 fn check_get(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 751 - self.scope.check(ctx)?; 816 + let scope_result = self.scope.check(ctx)?; 752 817 Ok(ExpressionResult { 753 818 is_local: false, 754 819 quantifier: One, // FIXME we don't really know 755 - used_captures: HashSet::new(), 820 + used_captures: scope_result.used_captures, 756 821 }) 757 822 } 758 823 }
+3
src/reference/mod.rs
··· 197 197 //! example stanza, whose query is `(identifier) @id`, `@id` would refer to the `identifier` syntax 198 198 //! node that the stanza matched against. 199 199 //! 200 + //! Unused query captures are considered errors, unless they start with an underscode. For example, 201 + //! a capture `@id` must be used within the stanza, but `@_id` does not. 202 + //! 200 203 //! # Variables 201 204 //! 202 205 //! You can use variables to pass information between different stanzas and statements in a graph
+25 -25
tests/it/execution.rs
··· 92 92 check_execution( 93 93 "pass", 94 94 indoc! {r#" 95 - (module) @root 95 + (module) 96 96 { 97 97 var new_node = #null 98 98 var current_node = (node) ··· 138 138 check_execution( 139 139 "pass", 140 140 indoc! {r#" 141 - (module) @root 141 + (module) 142 142 { 143 143 var current_node = (node) 144 144 ··· 257 257 indoc! {r#" 258 258 global filename 259 259 260 - (module) @root 260 + (module) 261 261 { 262 262 node n 263 263 attr (n) filename = filename ··· 277 277 indoc! {r#" 278 278 global pkgname = "" 279 279 280 - (module) @root 280 + (module) 281 281 { 282 282 node n 283 283 attr (n) pkgname = pkgname ··· 320 320 check_execution( 321 321 "pass", 322 322 indoc! {r#" 323 - (module) @root 323 + (module) 324 324 { 325 325 let x = (node) 326 326 let y = x ··· 338 338 check_execution( 339 339 "pass", 340 340 indoc! {r#" 341 - (module) @root 341 + (module) 342 342 { 343 343 node node0 344 344 attr (node0) val = (replace "accacc" (replace "abc" "b" "c") (replace "abc" "a" "b")) ··· 356 356 fail_execution( 357 357 "pass", 358 358 indoc! {r#" 359 - (module) @root 359 + (module) 360 360 { 361 361 scan "abc" { 362 362 "^\\b" { ··· 450 450 check_execution( 451 451 "pass", 452 452 indoc! {r#" 453 - (module (pass_statement)? @x) @root 453 + (module (pass_statement)? @x) 454 454 { 455 455 node node0 456 456 if some @x { ··· 472 472 check_execution( 473 473 "pass", 474 474 indoc! {r#" 475 - (module (import_statement)? @x) @root 475 + (module (import_statement)? @x) 476 476 { 477 477 node node0 478 478 if none @x { ··· 494 494 check_execution( 495 495 "pass", 496 496 indoc! {r#" 497 - (module (import_statement)? @x (pass_statement)? @y) @root 497 + (module (import_statement)? @x (pass_statement)? @y) 498 498 { 499 499 node node0 500 500 if none @x, some @y { ··· 516 516 check_execution( 517 517 "pass", 518 518 indoc! {r#" 519 - (module (import_statement)? @x (pass_statement)? @y) @root 519 + (module (import_statement)? @x (pass_statement)? @y) 520 520 { 521 521 node node0 522 522 if some @x { ··· 538 538 check_execution( 539 539 "pass", 540 540 indoc! {r#" 541 - (module (import_statement)? @x) @root 541 + (module (import_statement)? @x) 542 542 { 543 543 node node0 544 544 if some @x { ··· 560 560 check_execution( 561 561 "pass", 562 562 indoc! {r#" 563 - (module (import_statement)? @x) @root 563 + (module (import_statement)?) 564 564 { 565 565 node node0 566 566 if #true { ··· 582 582 check_execution( 583 583 "pass", 584 584 indoc! {r#" 585 - (module (import_statement)? @x (import_statement)? @y) @root 585 + (module (import_statement)? @x (import_statement)? @y) 586 586 { 587 587 node node0 588 588 if some @x { ··· 605 605 pass 606 606 "#, 607 607 indoc! {r#" 608 - (module (pass_statement)? @x) @root 608 + (module (pass_statement)? @x) 609 609 { 610 610 let n = 1 611 611 if some @x { ··· 629 629 pass 630 630 "#, 631 631 indoc! {r#" 632 - (module (pass_statement)? @x) @root 632 + (module (pass_statement)? @x) 633 633 { 634 634 var n = 1 635 635 if some @x { ··· 653 653 pass 654 654 "#, 655 655 indoc! {r#" 656 - (module (pass_statement)? @x) @root 656 + (module (pass_statement)? @x) 657 657 { 658 658 var n = 1 659 659 if some @x { ··· 679 679 pass 680 680 "#, 681 681 indoc! {r#" 682 - (module (pass_statement)* @xs) @root 682 + (module (pass_statement)* @xs) 683 683 { 684 684 var n = 0 685 685 for x in @xs { ··· 703 703 pass 704 704 "#, 705 705 indoc! {r#" 706 - (module (import_statement)* @xs) @root 706 + (module (import_statement)* @xs) 707 707 { 708 708 var n = 0 709 709 for x in @xs { ··· 727 727 pass 728 728 "#, 729 729 indoc! {r#" 730 - (module) @root 730 + (module) 731 731 { 732 732 var n = 0 733 733 for x in [#null, #null, #null] { ··· 751 751 pass 752 752 "#, 753 753 indoc! {r#" 754 - (module (pass_statement)* @xs) @root 754 + (module (pass_statement)* @xs) 755 755 { 756 756 let n = 1 757 757 for x in @xs { ··· 775 775 pass 776 776 "#, 777 777 indoc! {r#" 778 - (module (pass_statement)* @xs) @root 778 + (module (pass_statement)* @xs) 779 779 { 780 780 var n = 1 781 781 for x in @xs { ··· 801 801 pass 802 802 "#, 803 803 indoc! {r#" 804 - (module (pass_statement)+ @xs) @root 804 + (module (pass_statement)+ @xs) 805 805 { 806 806 var n = 0 807 807 for x in @xs { ··· 827 827 pass 828 828 "#, 829 829 indoc! {r#" 830 - (module (pass_statement)* @xs) @root 830 + (module (pass_statement)* @xs) 831 831 { 832 832 node node0 833 833 attr (node0) val = [ (named-child-index x) for x in @xs ] ··· 849 849 pass 850 850 "#, 851 851 indoc! {r#" 852 - (module (pass_statement)* @xs) @root 852 + (module (pass_statement)* @xs) 853 853 { 854 854 node node0 855 855 attr (node0) val = { (source-text x) for x in @xs }
+11 -11
tests/it/functions.rs
··· 60 60 check_execution( 61 61 "pass", 62 62 indoc! {r#" 63 - (module) @root 63 + (module) 64 64 { 65 65 node n 66 66 attr (n) eq = (eq #true #true) ··· 78 78 check_execution( 79 79 "pass", 80 80 indoc! {r#" 81 - (module) @root 81 + (module) 82 82 { 83 83 node n 84 84 attr (n) eq = (eq #true #false) ··· 96 96 fail_execution( 97 97 "pass", 98 98 indoc! {r#" 99 - (module) @root 99 + (module) 100 100 { 101 101 node n 102 102 attr (n) eq = (eq #true "false") ··· 110 110 check_execution( 111 111 "pass", 112 112 indoc! {r#" 113 - (module) @root 113 + (module) 114 114 { 115 115 node n 116 116 attr (n) str = (format "{} : {{ {} }}" "foo" #null) ··· 128 128 fail_execution( 129 129 "pass", 130 130 indoc! {r#" 131 - (module) @root 131 + (module) 132 132 { 133 133 node n 134 134 attr (n) str = (format "{} : {{ {} }}" "foo") ··· 142 142 fail_execution( 143 143 "pass", 144 144 indoc! {r#" 145 - (module) @root 145 + (module) 146 146 { 147 147 node n 148 148 attr (n) str = (format "{} : {{ {} }}" "foo" #null 42) ··· 156 156 fail_execution( 157 157 "pass", 158 158 indoc! {r#" 159 - (module) @root 159 + (module) 160 160 { 161 161 node n 162 162 attr (n) str = (format "{} : { {} }}" "foo" #null) ··· 170 170 fail_execution( 171 171 "pass", 172 172 indoc! {r#" 173 - (module) @root 173 + (module) 174 174 { 175 175 node n 176 176 attr (n) str = (format "{} : {{ {} }" "foo" #null) ··· 184 184 check_execution( 185 185 "pass", 186 186 indoc! {r#" 187 - (module) @root 187 + (module) 188 188 { 189 189 node n 190 190 attr (n) xs = (concat [1, 2] [] [3, 4, 5]) ··· 202 202 check_execution( 203 203 "pass", 204 204 indoc! {r#" 205 - (module) @root 205 + (module) 206 206 { 207 207 node n 208 208 attr (n) str = (join [1, 2, 3] ".") ··· 220 220 check_execution( 221 221 "pass", 222 222 indoc! {r#" 223 - (module) @root 223 + (module) 224 224 { 225 225 node n 226 226 attr (n) str = (join [1, 2, 3])
+41 -41
tests/it/lazy_execution.rs
··· 91 91 check_execution( 92 92 "pass", 93 93 indoc! {r#" 94 - (module) @root 94 + (module) 95 95 { 96 96 var new_node = #null 97 97 var current_node = (node) ··· 137 137 check_execution( 138 138 "pass", 139 139 indoc! {r#" 140 - (module) @root 140 + (module) 141 141 { 142 142 var current_node = (node) 143 143 ··· 258 258 indoc! {r#" 259 259 global filename 260 260 261 - (module) @root 261 + (module) 262 262 { 263 263 node n 264 264 attr (n) filename = filename ··· 278 278 indoc! {r#" 279 279 global pkgname = "" 280 280 281 - (module) @root 281 + (module) 282 282 { 283 283 node n 284 284 attr (n) pkgname = pkgname ··· 311 311 check_execution( 312 312 "pass", 313 313 indoc! {r#" 314 - (module) @root 314 + (module) 315 315 { 316 316 let x = (node) 317 317 let y = x ··· 334 334 check_execution( 335 335 "pass", 336 336 indoc! {r#" 337 - (module) @root 337 + (module) 338 338 { 339 339 node node0 340 340 attr (node0) val = (replace "accacc" (replace "abc" "b" "c") (replace "abc" "a" "b")) ··· 352 352 fail_execution( 353 353 "pass", 354 354 indoc! {r#" 355 - (module) @root 355 + (module) 356 356 { 357 357 scan "abc" { 358 358 "^\\b" { ··· 446 446 check_execution( 447 447 "pass", 448 448 indoc! {r#" 449 - (module (pass_statement)? @x) @root 449 + (module (pass_statement)? @x) 450 450 { 451 451 node node0 452 452 if some @x { ··· 468 468 check_execution( 469 469 "pass", 470 470 indoc! {r#" 471 - (module (import_statement)? @x) @root 471 + (module (import_statement)? @x) 472 472 { 473 473 node node0 474 474 if none @x { ··· 490 490 check_execution( 491 491 "pass", 492 492 indoc! {r#" 493 - (module (import_statement)? @x (pass_statement)? @y) @root 493 + (module (import_statement)? @x (pass_statement)? @y) 494 494 { 495 495 node node0 496 496 if none @x, some @y { ··· 512 512 check_execution( 513 513 "pass", 514 514 indoc! {r#" 515 - (module (import_statement)? @x (pass_statement)? @y) @root 515 + (module (import_statement)? @x (pass_statement)? @y) 516 516 { 517 517 node node0 518 518 if some @x { ··· 534 534 check_execution( 535 535 "pass", 536 536 indoc! {r#" 537 - (module (import_statement)? @x) @root 537 + (module (import_statement)? @x) 538 538 { 539 539 node node0 540 540 if some @x { ··· 556 556 check_execution( 557 557 "pass", 558 558 indoc! {r#" 559 - (module (import_statement)? @x) @root 559 + (module (import_statement)?) 560 560 { 561 561 node node0 562 562 if #true { ··· 578 578 check_execution( 579 579 "pass", 580 580 indoc! {r#" 581 - (module (import_statement)? @x (import_statement)? @y) @root 581 + (module (import_statement)? @x (import_statement)? @y) 582 582 { 583 583 node node0 584 584 if some @x { ··· 601 601 pass 602 602 "#, 603 603 indoc! {r#" 604 - (module (pass_statement)? @x) @root 604 + (module (pass_statement)? @x) 605 605 { 606 606 let n = 1 607 607 if some @x { ··· 625 625 pass 626 626 "#, 627 627 indoc! {r#" 628 - (module (pass_statement)? @x) @root 628 + (module (pass_statement)? @x) 629 629 { 630 630 var n = 1 631 631 if some @x { ··· 649 649 pass 650 650 "#, 651 651 indoc! {r#" 652 - (module (pass_statement)? @x) @root 652 + (module (pass_statement)? @x) 653 653 { 654 654 var n = 1 655 655 if some @x { ··· 675 675 pass 676 676 "#, 677 677 indoc! {r#" 678 - (module (pass_statement)* @xs) @root 678 + (module (pass_statement)* @xs) 679 679 { 680 680 var n = 0 681 681 for x in @xs { ··· 699 699 pass 700 700 "#, 701 701 indoc! {r#" 702 - (module (import_statement)* @xs) @root 702 + (module (import_statement)* @xs) 703 703 { 704 704 var n = 0 705 705 for x in @xs { ··· 723 723 pass 724 724 "#, 725 725 indoc! {r#" 726 - (module) @root 726 + (module) 727 727 { 728 728 var n = 0 729 729 for x in [#null, #null, #null] { ··· 747 747 pass 748 748 "#, 749 749 indoc! {r#" 750 - (module (pass_statement)* @xs) @root 750 + (module (pass_statement)* @xs) 751 751 { 752 752 let n = 1 753 753 for x in @xs { ··· 771 771 pass 772 772 "#, 773 773 indoc! {r#" 774 - (module (pass_statement)* @xs) @root 774 + (module (pass_statement)* @xs) 775 775 { 776 776 var n = 1 777 777 for x in @xs { ··· 797 797 pass 798 798 "#, 799 799 indoc! {r#" 800 - (module (pass_statement)+ @xs) @root 800 + (module (pass_statement)+ @xs) 801 801 { 802 802 var n = 0 803 803 for x in @xs { ··· 823 823 pass 824 824 "#, 825 825 indoc! {r#" 826 - (module (pass_statement)* @xs) @root 826 + (module (pass_statement)* @xs) 827 827 { 828 828 node node0 829 829 attr (node0) val = [ (named-child-index x) for x in @xs ] ··· 845 845 pass 846 846 "#, 847 847 indoc! {r#" 848 - (module (pass_statement)* @xs) @root 848 + (module (pass_statement)* @xs) 849 849 { 850 850 node node0 851 851 attr (node0) val = { (source-text x) for x in @xs } ··· 916 916 check_execution( 917 917 "pass", 918 918 indoc! {r#" 919 - (module) @root 919 + (module) 920 920 { 921 921 node node0 922 922 } ··· 951 951 check_execution( 952 952 "pass", 953 953 indoc! {r#" 954 - (module) @root 954 + (module) 955 955 { 956 956 node node0 957 957 node node1 ··· 971 971 check_execution( 972 972 "pass", 973 973 indoc! {r#" 974 - (module) @root 974 + (module) 975 975 { 976 976 node node0 977 977 node node1 ··· 997 997 check_execution( 998 998 "pass", 999 999 indoc! {r#" 1000 - (module) @root 1000 + (module) 1001 1001 { 1002 1002 var node = #null 1003 1003 ··· 1063 1063 print(a.d.f) 1064 1064 "#}, 1065 1065 indoc! {r#" 1066 - (call function:(_)@fun arguments: (argument_list (_)@arg)) { 1066 + (call function:(_) arguments: (argument_list (_)@arg)) { 1067 1067 ; let @arg.no_object.lala = 3 ; error 1068 1068 let @arg.object.lala = 3 1069 1069 let @arg.object.object.lala = 12 ··· 1083 1083 check_execution( 1084 1084 "pass", 1085 1085 indoc! {r#" 1086 - (module) @root 1086 + (module) 1087 1087 { 1088 1088 node n 1089 1089 ··· 1110 1110 check_execution( 1111 1111 "pass", 1112 1112 indoc! {r#" 1113 - (module) @root 1113 + (module) 1114 1114 { 1115 1115 node n 1116 1116 ··· 1137 1137 check_execution( 1138 1138 "pass", 1139 1139 indoc! {r#" 1140 - (module) @root 1140 + (module) 1141 1141 { 1142 1142 node n 1143 1143 ··· 1177 1177 check_execution( 1178 1178 "pass", 1179 1179 indoc! {r#" 1180 - (module) @root 1180 + (module) 1181 1181 { 1182 1182 node n 1183 1183 ··· 1217 1217 check_execution( 1218 1218 "pass", 1219 1219 indoc! {r#" 1220 - (module) @root 1220 + (module) 1221 1221 { 1222 1222 node n 1223 1223 ··· 1256 1256 check_execution( 1257 1257 "pass", 1258 1258 indoc! {r#" 1259 - (module) @root 1259 + (module) 1260 1260 { 1261 1261 var x = 0 1262 1262 var y = 0 ··· 1286 1286 check_execution( 1287 1287 "pass", 1288 1288 indoc! {r#" 1289 - (module) @root 1289 + (module) 1290 1290 { 1291 1291 var x = 0 1292 1292 var y = 0 ··· 1322 1322 check_execution( 1323 1323 "pass", 1324 1324 indoc! {r#" 1325 - (module) @root 1325 + (module) 1326 1326 { 1327 1327 var x = 0 1328 1328 ··· 1353 1353 check_execution( 1354 1354 "pass", 1355 1355 indoc! {r#" 1356 - (module) @root 1356 + (module) 1357 1357 { 1358 1358 var x = 0 1359 1359 var y = 0 ··· 1394 1394 check_execution( 1395 1395 "pass", 1396 1396 indoc! {r#" 1397 - (module) @root 1397 + (module) 1398 1398 { 1399 1399 let x = (node) 1400 1400 attr ((node)) ref = x ··· 1416 1416 check_execution( 1417 1417 "pass", 1418 1418 indoc! {r#" 1419 - (module) @root 1419 + (module) 1420 1420 { 1421 1421 var x = #null 1422 1422 set x = (node)
+23 -3
tests/it/parser.rs
··· 16 16 fn can_parse_blocks() { 17 17 let source = r#" 18 18 (function_definition 19 - name: (identifier) @cap1) @cap2 19 + name: (identifier) @_cap1) @cap2 20 20 { 21 21 node loc1 22 22 node @cap2.prop1 ··· 1079 1079 #[test] 1080 1080 fn can_parse_list_comprehension() { 1081 1081 let source = r#" 1082 - (module (_)* @xs)@mod 1082 + (module (_)* @xs) 1083 1083 { 1084 1084 print [ (named-child-index x) for x in @xs ] 1085 1085 } ··· 1132 1132 #[test] 1133 1133 fn can_parse_set_comprehension() { 1134 1134 let source = r#" 1135 - (module (_)* @xs)@mod 1135 + (module (_)* @xs) 1136 1136 { 1137 1137 print { (named-child-index x) for x in @xs } 1138 1138 } ··· 1579 1579 assert_eq!(err.column, 13, "expected column 13, got {}", err.column); 1580 1580 assert_eq!(err.offset, 112, "expected offset 112, got {}", err.offset); 1581 1581 } 1582 + 1583 + #[test] 1584 + fn cannot_parse_unused_capture() { 1585 + let source = r#" 1586 + (function_definition name: (identifier) @name) { 1587 + } 1588 + "#; 1589 + if let Ok(_) = File::from_str(tree_sitter_python::language(), source) { 1590 + panic!("Parse succeeded unexpectedly"); 1591 + } 1592 + } 1593 + 1594 + #[test] 1595 + fn can_parse_explicitly_unused_capture() { 1596 + let source = r#" 1597 + (function_definition name: (identifier) @_name) { 1598 + } 1599 + "#; 1600 + File::from_str(tree_sitter_python::language(), source).expect("parse to succeed"); 1601 + }