fork of https://github.com/tree-sitter/tree-sitter-graph
at main 30 kB view raw
1// -*- coding: utf-8 -*- 2// ------------------------------------------------------------------------------------------------ 3// Copyright © 2022, 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 std::collections::HashSet; 9use std::path::Path; 10 11use thiserror::Error; 12use tree_sitter::CaptureQuantifier; 13use tree_sitter::CaptureQuantifier::One; 14use tree_sitter::CaptureQuantifier::OneOrMore; 15use tree_sitter::CaptureQuantifier::ZeroOrMore; 16use tree_sitter::CaptureQuantifier::ZeroOrOne; 17use tree_sitter::Query; 18 19use crate::ast; 20use crate::parse_error::Excerpt; 21use crate::parser::FULL_MATCH; 22use crate::variables::MutVariables; 23use crate::variables::VariableError; 24use crate::variables::VariableMap; 25use crate::variables::Variables; 26use crate::Identifier; 27use crate::Location; 28 29#[derive(Debug, Error)] 30pub enum CheckError { 31 #[error("Cannot hide global variable {0} at {1}")] 32 CannotHideGlobalVariable(String, Location), 33 #[error("Cannot set global variable {0} at {1}")] 34 CannotSetGlobalVariable(String, Location), 35 #[error("Duplicate global variable {0} at {1}")] 36 DuplicateGlobalVariable(String, Location), 37 #[error("Expected list value at {0}")] 38 ExpectedListValue(Location), 39 #[error("Expected local value at {0}")] 40 ExpectedLocalValue(Location), 41 #[error("Expected optional value at {0}")] 42 ExpectedOptionalValue(Location), 43 #[error("Nullable regular expression /{0}/ at {1}")] 44 NullableRegex(String, Location), 45 #[error("Undefined syntax capture @{0} at {1}")] 46 UndefinedSyntaxCapture(String, Location), 47 #[error("Undefined variable {0} at {1}")] 48 UndefinedVariable(String, Location), 49 #[error("Unused capture(s) {0} at {1}. Remove or prefix with _.")] 50 UnusedCaptures(String, Location), 51 #[error("{0}: {1} at {2}")] 52 Variable(VariableError, String, Location), 53} 54 55impl CheckError { 56 pub fn display_pretty<'a>( 57 &'a self, 58 path: &'a Path, 59 source: &'a str, 60 ) -> impl std::fmt::Display + 'a { 61 DisplayCheckErrorPretty { 62 error: self, 63 path, 64 source, 65 } 66 } 67} 68 69struct DisplayCheckErrorPretty<'a> { 70 error: &'a CheckError, 71 path: &'a Path, 72 source: &'a str, 73} 74 75impl std::fmt::Display for DisplayCheckErrorPretty<'_> { 76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 77 let location = match self.error { 78 CheckError::CannotHideGlobalVariable(_, location) => *location, 79 CheckError::CannotSetGlobalVariable(_, location) => *location, 80 CheckError::DuplicateGlobalVariable(_, location) => *location, 81 CheckError::ExpectedListValue(location) => *location, 82 CheckError::ExpectedLocalValue(location) => *location, 83 CheckError::ExpectedOptionalValue(location) => *location, 84 CheckError::NullableRegex(_, location) => *location, 85 CheckError::UndefinedSyntaxCapture(_, location) => *location, 86 CheckError::UndefinedVariable(_, location) => *location, 87 CheckError::UnusedCaptures(_, location) => *location, 88 CheckError::Variable(_, _, location) => *location, 89 }; 90 writeln!(f, "{}", self.error)?; 91 write!( 92 f, 93 "{}", 94 Excerpt::from_source( 95 self.path, 96 self.source, 97 location.row, 98 location.to_column_range(), 99 0 100 ) 101 )?; 102 Ok(()) 103 } 104} 105 106/// Checker context 107struct CheckContext<'a> { 108 globals: &'a dyn Variables<VariableResult>, 109 file_query: &'a Query, 110 stanza_index: usize, 111 stanza_query: &'a Query, 112 locals: &'a mut dyn MutVariables<VariableResult>, 113} 114 115#[derive(Clone, Debug)] 116struct VariableResult { 117 is_local: bool, 118 quantifier: CaptureQuantifier, 119} 120 121//----------------------------------------------------------------------------- 122// File 123 124impl ast::File { 125 pub fn check(&mut self) -> Result<(), CheckError> { 126 let mut globals = VariableMap::new(); 127 for global in &self.globals { 128 globals 129 .add( 130 global.name.clone(), 131 VariableResult { 132 quantifier: global.quantifier, 133 is_local: true, 134 }, 135 false, 136 ) 137 .map_err(|_| { 138 CheckError::DuplicateGlobalVariable( 139 global.name.as_str().to_string(), 140 global.location, 141 ) 142 })?; 143 } 144 let file_query = self.query.as_ref().unwrap(); 145 for (index, stanza) in self.stanzas.iter_mut().enumerate() { 146 stanza.check(&globals, file_query, index)?; 147 } 148 Ok(()) 149 } 150} 151 152//----------------------------------------------------------------------------- 153// Stanza 154 155impl ast::Stanza { 156 fn check( 157 &mut self, 158 globals: &dyn Variables<VariableResult>, 159 file_query: &Query, 160 stanza_index: usize, 161 ) -> Result<(), CheckError> { 162 let mut locals = VariableMap::new(); 163 let mut ctx = CheckContext { 164 globals, 165 file_query, 166 stanza_index, 167 stanza_query: &self.query, 168 locals: &mut locals, 169 }; 170 self.full_match_file_capture_index = 171 ctx.file_query 172 .capture_index_for_name(FULL_MATCH) 173 .expect("missing capture index for full match") as usize; 174 175 let mut used_captures = HashSet::new(); 176 for statement in &mut self.statements { 177 let stmt_result = statement.check(&mut ctx)?; 178 used_captures.extend(stmt_result.used_captures); 179 } 180 181 let all_captures = self 182 .query 183 .capture_names() 184 .into_iter() 185 .filter(|cn| { 186 self.query 187 .capture_index_for_name(cn) 188 .expect("capture should have index") 189 != self.full_match_stanza_capture_index as u32 190 }) 191 .map(|cn| Identifier::from(*cn)) 192 .collect::<HashSet<_>>(); 193 let unused_captures = all_captures 194 .difference(&used_captures) 195 .filter(|i| !i.starts_with("_")) 196 .map(|i| format!("@{}", i)) 197 .collect::<Vec<_>>(); 198 if !unused_captures.is_empty() { 199 return Err(CheckError::UnusedCaptures( 200 unused_captures.join(" "), 201 self.range.start, 202 )); 203 } 204 205 Ok(()) 206 } 207} 208 209//----------------------------------------------------------------------------- 210// Statements 211 212#[derive(Clone, Debug)] 213struct StatementResult { 214 used_captures: HashSet<Identifier>, 215} 216 217impl ast::Statement { 218 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 219 match self { 220 Self::DeclareImmutable(stmt) => stmt.check(ctx), 221 Self::DeclareMutable(stmt) => stmt.check(ctx), 222 Self::Assign(stmt) => stmt.check(ctx), 223 Self::Expr(stmt) => stmt.check(ctx), 224 Self::CreateGraphNode(stmt) => stmt.check(ctx), 225 Self::AddGraphNodeAttribute(stmt) => stmt.check(ctx), 226 Self::CreateEdge(stmt) => stmt.check(ctx), 227 Self::AddEdgeAttribute(stmt) => stmt.check(ctx), 228 Self::Scan(stmt) => stmt.check(ctx), 229 Self::Print(stmt) => stmt.check(ctx), 230 Self::If(stmt) => stmt.check(ctx), 231 Self::ForIn(stmt) => stmt.check(ctx), 232 } 233 } 234} 235 236impl ast::DeclareImmutable { 237 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 238 let mut used_captures = HashSet::new(); 239 let value = self.value.check(ctx)?; 240 used_captures.extend(value.used_captures.iter().cloned()); 241 let var_result = self.variable.check_add(ctx, value.into(), false)?; 242 used_captures.extend(var_result.used_captures); 243 Ok(StatementResult { used_captures }) 244 } 245} 246 247impl ast::DeclareMutable { 248 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 249 let mut used_captures = HashSet::new(); 250 let value = self.value.check(ctx)?; 251 used_captures.extend(value.used_captures.iter().cloned()); 252 let var_result = self.variable.check_add(ctx, value.into(), true)?; 253 used_captures.extend(var_result.used_captures); 254 Ok(StatementResult { used_captures }) 255 } 256} 257 258impl ast::Assign { 259 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 260 let mut used_captures = HashSet::new(); 261 let value = self.value.check(ctx)?; 262 used_captures.extend(value.used_captures.iter().cloned()); 263 let var_result = self.variable.check_set(ctx, value.into())?; 264 used_captures.extend(var_result.used_captures); 265 Ok(StatementResult { used_captures }) 266 } 267} 268 269impl ast::ExpressionStatement { 270 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 271 let mut used_captures = HashSet::new(); 272 let value = self.value.check(ctx)?; 273 used_captures.extend(value.used_captures.iter().cloned()); 274 Ok(StatementResult { used_captures }) 275 } 276} 277 278impl ast::CreateGraphNode { 279 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 280 let node_result = self.node.check_add( 281 ctx, 282 VariableResult { 283 is_local: true, 284 quantifier: One, 285 }, 286 false, 287 )?; 288 Ok(StatementResult { 289 used_captures: node_result.used_captures, 290 }) 291 } 292} 293 294impl ast::AddGraphNodeAttribute { 295 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 296 let mut used_captures = HashSet::new(); 297 let node_result = self.node.check(ctx)?; 298 used_captures.extend(node_result.used_captures); 299 for attribute in &mut self.attributes { 300 let attr_result = attribute.check(ctx)?; 301 used_captures.extend(attr_result.used_captures); 302 } 303 Ok(StatementResult { used_captures }) 304 } 305} 306 307impl ast::CreateEdge { 308 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 309 let mut used_captures = HashSet::new(); 310 let source_result = self.source.check(ctx)?; 311 used_captures.extend(source_result.used_captures); 312 let sink_result = self.sink.check(ctx)?; 313 used_captures.extend(sink_result.used_captures); 314 Ok(StatementResult { used_captures }) 315 } 316} 317 318impl ast::AddEdgeAttribute { 319 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 320 let mut used_captures = HashSet::new(); 321 let source_result = self.source.check(ctx)?; 322 used_captures.extend(source_result.used_captures); 323 let sink_result = self.sink.check(ctx)?; 324 used_captures.extend(sink_result.used_captures); 325 for attribute in &mut self.attributes { 326 let attr_result = attribute.check(ctx)?; 327 used_captures.extend(attr_result.used_captures); 328 } 329 Ok(StatementResult { used_captures }) 330 } 331} 332 333impl ast::Scan { 334 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 335 let mut used_captures = HashSet::new(); 336 337 let value_result = self.value.check(ctx)?; 338 if !value_result.is_local { 339 return Err(CheckError::ExpectedLocalValue(self.location)); 340 } 341 used_captures.extend(value_result.used_captures); 342 343 for arm in &mut self.arms { 344 // Be aware that this check is not complete, as it does not rule out 345 // all regular expressions that admit empty matches. For example, th 346 // regex "\b" matches empty strings within a larger non-empty one. 347 // Therefore, there is also a runtime check that checks that a match was 348 // non-empty. This is all to prevent non-termination of scan. 349 if let Some(_) = arm.regex.captures("") { 350 return Err(CheckError::NullableRegex( 351 arm.regex.to_string(), 352 arm.location, 353 )); 354 } 355 356 let mut arm_locals = VariableMap::nested(ctx.locals); 357 let mut arm_ctx = CheckContext { 358 globals: ctx.globals, 359 file_query: ctx.file_query, 360 stanza_index: ctx.stanza_index, 361 stanza_query: ctx.stanza_query, 362 locals: &mut arm_locals, 363 }; 364 365 for statement in &mut arm.statements { 366 let stmt_result = statement.check(&mut arm_ctx)?; 367 used_captures.extend(stmt_result.used_captures); 368 } 369 } 370 Ok(StatementResult { used_captures }) 371 } 372} 373 374impl ast::Print { 375 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 376 let mut used_captures = HashSet::new(); 377 for value in &mut self.values { 378 let value_result = value.check(ctx)?; 379 used_captures.extend(value_result.used_captures); 380 } 381 Ok(StatementResult { used_captures }) 382 } 383} 384 385impl ast::If { 386 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 387 let mut used_captures = HashSet::new(); 388 389 for arm in &mut self.arms { 390 for condition in &mut arm.conditions { 391 let condition_result = condition.check(ctx)?; 392 used_captures.extend(condition_result.used_captures); 393 } 394 395 let mut arm_locals = VariableMap::nested(ctx.locals); 396 let mut arm_ctx = CheckContext { 397 globals: ctx.globals, 398 file_query: ctx.file_query, 399 stanza_index: ctx.stanza_index, 400 stanza_query: ctx.stanza_query, 401 locals: &mut arm_locals, 402 }; 403 404 for statement in &mut arm.statements { 405 let stmt_result = statement.check(&mut arm_ctx)?; 406 used_captures.extend(stmt_result.used_captures); 407 } 408 } 409 Ok(StatementResult { used_captures }) 410 } 411} 412 413impl ast::Condition { 414 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 415 let mut used_captures = HashSet::new(); 416 match self { 417 Self::None { value, location } | Self::Some { value, location } => { 418 let value_result = value.check(ctx)?; 419 if !value_result.is_local { 420 return Err(CheckError::ExpectedLocalValue(*location)); 421 } 422 if value_result.quantifier != ZeroOrOne { 423 return Err(CheckError::ExpectedOptionalValue(*location)); 424 } 425 used_captures.extend(value_result.used_captures); 426 } 427 Self::Bool { value, location } => { 428 let value_result = value.check(ctx)?; 429 if !value_result.is_local { 430 return Err(CheckError::ExpectedLocalValue(*location)); 431 } 432 used_captures.extend(value_result.used_captures); 433 } 434 } 435 Ok(StatementResult { used_captures }) 436 } 437} 438 439impl ast::ForIn { 440 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 441 let mut used_captures = HashSet::new(); 442 443 let value_result = self.value.check(ctx)?; 444 if !value_result.is_local { 445 return Err(CheckError::ExpectedLocalValue(self.location)); 446 } 447 if value_result.quantifier != ZeroOrMore && value_result.quantifier != OneOrMore { 448 return Err(CheckError::ExpectedListValue(self.location)); 449 } 450 used_captures.extend(value_result.used_captures.iter().cloned()); 451 452 let mut loop_locals = VariableMap::nested(ctx.locals); 453 let mut loop_ctx = CheckContext { 454 globals: ctx.globals, 455 file_query: ctx.file_query, 456 stanza_index: ctx.stanza_index, 457 stanza_query: ctx.stanza_query, 458 locals: &mut loop_locals, 459 }; 460 let var_result = self 461 .variable 462 .check_add(&mut loop_ctx, value_result.into(), false)?; 463 used_captures.extend(var_result.used_captures); 464 465 for statement in &mut self.statements { 466 let stmt_result = statement.check(&mut loop_ctx)?; 467 used_captures.extend(stmt_result.used_captures); 468 } 469 470 Ok(StatementResult { used_captures }) 471 } 472} 473 474//----------------------------------------------------------------------------- 475// Expressions 476 477/// Expression checking result 478#[derive(Clone, Debug)] 479struct ExpressionResult { 480 is_local: bool, 481 quantifier: CaptureQuantifier, 482 used_captures: HashSet<Identifier>, 483} 484 485impl ast::Expression { 486 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 487 match self { 488 Self::FalseLiteral => Ok(ExpressionResult { 489 is_local: true, 490 quantifier: One, 491 used_captures: HashSet::default(), 492 }), 493 Self::NullLiteral => Ok(ExpressionResult { 494 is_local: true, 495 quantifier: One, 496 used_captures: HashSet::default(), 497 }), 498 Self::TrueLiteral => Ok(ExpressionResult { 499 is_local: true, 500 quantifier: One, 501 used_captures: HashSet::default(), 502 }), 503 Self::IntegerConstant(expr) => expr.check(ctx), 504 Self::StringConstant(expr) => expr.check(ctx), 505 Self::ListLiteral(expr) => expr.check(ctx), 506 Self::SetLiteral(expr) => expr.check(ctx), 507 Self::ListComprehension(expr) => expr.check(ctx), 508 Self::SetComprehension(expr) => expr.check(ctx), 509 Self::Capture(expr) => expr.check(ctx), 510 Self::Variable(expr) => expr.check_get(ctx), 511 Self::Call(expr) => expr.check(ctx), 512 Self::RegexCapture(expr) => expr.check(ctx), 513 } 514 } 515} 516 517impl ast::IntegerConstant { 518 fn check(&mut self, _ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 519 Ok(ExpressionResult { 520 is_local: true, 521 quantifier: One, 522 used_captures: HashSet::default(), 523 }) 524 } 525} 526 527impl ast::StringConstant { 528 fn check(&mut self, _ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 529 Ok(ExpressionResult { 530 is_local: true, 531 quantifier: One, 532 used_captures: HashSet::default(), 533 }) 534 } 535} 536 537impl ast::ListLiteral { 538 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 539 let mut is_local = true; 540 let mut used_captures = HashSet::new(); 541 for element in &mut self.elements { 542 let element_result = element.check(ctx)?; 543 is_local &= element_result.is_local; 544 used_captures.extend(element_result.used_captures); 545 } 546 Ok(ExpressionResult { 547 is_local, 548 quantifier: ZeroOrMore, 549 used_captures, 550 }) 551 } 552} 553 554impl ast::SetLiteral { 555 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 556 let mut is_local = true; 557 let mut used_captures = HashSet::new(); 558 for element in &mut self.elements { 559 let element_result = element.check(ctx)?; 560 is_local &= element_result.is_local; 561 used_captures.extend(element_result.used_captures); 562 } 563 Ok(ExpressionResult { 564 is_local, 565 quantifier: ZeroOrMore, 566 used_captures, 567 }) 568 } 569} 570 571impl ast::ListComprehension { 572 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 573 let mut used_captures = HashSet::new(); 574 575 let value_result = self.value.check(ctx)?; 576 if !value_result.is_local { 577 return Err(CheckError::ExpectedLocalValue(self.location)); 578 } 579 if value_result.quantifier != ZeroOrMore && value_result.quantifier != OneOrMore { 580 return Err(CheckError::ExpectedListValue(self.location)); 581 } 582 used_captures.extend(value_result.used_captures.iter().cloned()); 583 584 let mut loop_locals = VariableMap::nested(ctx.locals); 585 let mut loop_ctx = CheckContext { 586 globals: ctx.globals, 587 file_query: ctx.file_query, 588 stanza_index: ctx.stanza_index, 589 stanza_query: ctx.stanza_query, 590 locals: &mut loop_locals, 591 }; 592 let var_result = self 593 .variable 594 .check_add(&mut loop_ctx, value_result.into(), false)?; 595 used_captures.extend(var_result.used_captures); 596 597 let element_result = self.element.check(&mut loop_ctx)?; 598 used_captures.extend(element_result.used_captures); 599 600 Ok(ExpressionResult { 601 is_local: element_result.is_local, 602 quantifier: ZeroOrMore, 603 used_captures, 604 }) 605 } 606} 607 608impl ast::SetComprehension { 609 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 610 let mut used_captures = HashSet::new(); 611 612 let value_result = self.value.check(ctx)?; 613 if !value_result.is_local { 614 return Err(CheckError::ExpectedLocalValue(self.location)); 615 } 616 if value_result.quantifier != ZeroOrMore && value_result.quantifier != OneOrMore { 617 return Err(CheckError::ExpectedListValue(self.location)); 618 } 619 used_captures.extend(value_result.used_captures.iter().cloned()); 620 621 let mut loop_locals = VariableMap::nested(ctx.locals); 622 let mut loop_ctx = CheckContext { 623 globals: ctx.globals, 624 file_query: ctx.file_query, 625 stanza_index: ctx.stanza_index, 626 stanza_query: ctx.stanza_query, 627 locals: &mut loop_locals, 628 }; 629 let var_result = self 630 .variable 631 .check_add(&mut loop_ctx, value_result.into(), false)?; 632 used_captures.extend(var_result.used_captures); 633 634 let element_result = self.element.check(&mut loop_ctx)?; 635 used_captures.extend(element_result.used_captures); 636 637 Ok(ExpressionResult { 638 is_local: element_result.is_local, 639 quantifier: ZeroOrMore, 640 used_captures, 641 }) 642 } 643} 644 645impl ast::Capture { 646 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 647 let name = self.name.to_string(); 648 self.stanza_capture_index = ctx 649 .stanza_query 650 .capture_index_for_name(&name) 651 .ok_or_else(|| CheckError::UndefinedSyntaxCapture(name.clone(), self.location))? 652 as usize; 653 self.file_capture_index = ctx 654 .file_query 655 .capture_index_for_name(&name) 656 .expect("missing capture index for name") as usize; // if the previous lookup succeeded, this one should succeed as well 657 self.quantifier = 658 ctx.file_query.capture_quantifiers(ctx.stanza_index)[self.file_capture_index]; 659 Ok(ExpressionResult { 660 is_local: true, 661 quantifier: self.quantifier, 662 used_captures: HashSet::from([self.name.clone()]), 663 }) 664 } 665} 666 667impl ast::Call { 668 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 669 let mut is_local = true; 670 let mut used_captures = HashSet::new(); 671 for parameter in &mut self.parameters { 672 let parameter_result = parameter.check(ctx)?; 673 is_local &= parameter_result.is_local; 674 used_captures.extend(parameter_result.used_captures); 675 } 676 Ok(ExpressionResult { 677 is_local, 678 quantifier: One, // FIXME we don't really know 679 used_captures, 680 }) 681 } 682} 683 684impl ast::RegexCapture { 685 fn check(&mut self, _ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 686 Ok(ExpressionResult { 687 is_local: true, 688 quantifier: One, 689 used_captures: HashSet::default(), 690 }) 691 } 692} 693 694//----------------------------------------------------------------------------- 695// Variables 696 697impl ast::Variable { 698 fn check_add( 699 &mut self, 700 ctx: &mut CheckContext, 701 value: VariableResult, 702 mutable: bool, 703 ) -> Result<StatementResult, CheckError> { 704 match self { 705 Self::Unscoped(v) => v.check_add(ctx, value, mutable), 706 Self::Scoped(v) => v.check_add(ctx, value, mutable), 707 } 708 } 709 710 fn check_set( 711 &mut self, 712 ctx: &mut CheckContext, 713 value: VariableResult, 714 ) -> Result<StatementResult, CheckError> { 715 match self { 716 Self::Unscoped(v) => v.check_set(ctx, value), 717 Self::Scoped(v) => v.check_set(ctx, value), 718 } 719 } 720 721 fn check_get(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 722 match self { 723 Self::Unscoped(v) => v.check_get(ctx), 724 Self::Scoped(v) => v.check_get(ctx), 725 } 726 } 727} 728 729impl ast::UnscopedVariable { 730 fn check_add( 731 &mut self, 732 ctx: &mut CheckContext, 733 value: VariableResult, 734 mutable: bool, 735 ) -> Result<StatementResult, CheckError> { 736 if ctx.globals.get(&self.name).is_some() { 737 return Err(CheckError::CannotHideGlobalVariable( 738 self.name.as_str().to_string(), 739 self.location, 740 )); 741 } 742 let mut value = value; 743 // Mutable variables are not considered local, because a non-local 744 // assignment in a loop could invalidate an earlier local assignment. 745 // Since we process all statement in order, we don't have info on later 746 // assignments, and can assume non-local to be sound. 747 if mutable { 748 value.is_local = false; 749 } 750 ctx.locals 751 .add(self.name.clone(), value, mutable) 752 .map_err(|e| CheckError::Variable(e, format!("{}", self.name), self.location))?; 753 Ok(StatementResult { 754 used_captures: HashSet::default(), 755 }) 756 } 757 758 fn check_set( 759 &mut self, 760 ctx: &mut CheckContext, 761 value: VariableResult, 762 ) -> Result<StatementResult, CheckError> { 763 if ctx.globals.get(&self.name).is_some() { 764 return Err(CheckError::CannotSetGlobalVariable( 765 self.name.as_str().to_string(), 766 self.location, 767 )); 768 } 769 let mut value = value; 770 // Mutable variables are not considered local, because a non-local 771 // assignment in a loop could invalidate an earlier local assignment. 772 // Since we process all statement in order, we don't have info on later 773 // assignments, and can assume non-local to be sound. 774 value.is_local = false; 775 ctx.locals 776 .set(self.name.clone(), value) 777 .map_err(|e| CheckError::Variable(e, format!("{}", self.name), self.location))?; 778 Ok(StatementResult { 779 used_captures: HashSet::default(), 780 }) 781 } 782 783 fn check_get(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 784 if let Some(result) = ctx.globals.get(&self.name) { 785 Some(result) 786 } else { 787 ctx.locals.get(&self.name) 788 } 789 .map(|value| value.into()) 790 .ok_or_else(|| CheckError::UndefinedVariable(self.name.as_str().to_string(), self.location)) 791 } 792} 793 794impl ast::ScopedVariable { 795 fn check_add( 796 &mut self, 797 ctx: &mut CheckContext, 798 _value: VariableResult, 799 _mutable: bool, 800 ) -> Result<StatementResult, CheckError> { 801 let scope_result = self.scope.check(ctx)?; 802 Ok(scope_result.into()) 803 } 804 805 fn check_set( 806 &mut self, 807 ctx: &mut CheckContext, 808 _value: VariableResult, 809 ) -> Result<StatementResult, CheckError> { 810 let scope_result = self.scope.check(ctx)?; 811 Ok(scope_result.into()) 812 } 813 814 fn check_get(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 815 let scope_result = self.scope.check(ctx)?; 816 Ok(ExpressionResult { 817 is_local: false, 818 quantifier: One, // FIXME we don't really know 819 used_captures: scope_result.used_captures, 820 }) 821 } 822} 823 824//----------------------------------------------------------------------------- 825// Attributes 826 827#[derive(Clone, Debug)] 828struct AttributeResult { 829 used_captures: HashSet<Identifier>, 830} 831 832impl ast::Attribute { 833 fn check(&mut self, ctx: &mut CheckContext) -> Result<AttributeResult, CheckError> { 834 let value_result = self.value.check(ctx)?; 835 Ok(AttributeResult { 836 used_captures: value_result.used_captures, 837 }) 838 } 839} 840 841//----------------------------------------------------------------------------- 842// Result Conversions 843 844impl Into<StatementResult> for ExpressionResult { 845 fn into(self) -> StatementResult { 846 StatementResult { 847 used_captures: self.used_captures, 848 } 849 } 850} 851 852impl Into<ExpressionResult> for &VariableResult { 853 fn into(self) -> ExpressionResult { 854 ExpressionResult { 855 is_local: self.is_local, 856 quantifier: self.quantifier, 857 used_captures: HashSet::default(), 858 } 859 } 860} 861 862impl Into<VariableResult> for ExpressionResult { 863 fn into(self) -> VariableResult { 864 VariableResult { 865 is_local: self.is_local, 866 quantifier: self.quantifier, 867 } 868 } 869}