fork of https://github.com/tree-sitter/tree-sitter-graph
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.as_str())) 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.location, 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::CreateGraphNode(stmt) => stmt.check(ctx), 224 Self::AddGraphNodeAttribute(stmt) => stmt.check(ctx), 225 Self::CreateEdge(stmt) => stmt.check(ctx), 226 Self::AddEdgeAttribute(stmt) => stmt.check(ctx), 227 Self::Scan(stmt) => stmt.check(ctx), 228 Self::Print(stmt) => stmt.check(ctx), 229 Self::If(stmt) => stmt.check(ctx), 230 Self::ForIn(stmt) => stmt.check(ctx), 231 } 232 } 233} 234 235impl ast::DeclareImmutable { 236 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 237 let mut used_captures = HashSet::new(); 238 let value = self.value.check(ctx)?; 239 used_captures.extend(value.used_captures.iter().cloned()); 240 let var_result = self.variable.check_add(ctx, value.into(), false)?; 241 used_captures.extend(var_result.used_captures); 242 Ok(StatementResult { used_captures }) 243 } 244} 245 246impl ast::DeclareMutable { 247 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 248 let mut used_captures = HashSet::new(); 249 let value = self.value.check(ctx)?; 250 used_captures.extend(value.used_captures.iter().cloned()); 251 let var_result = self.variable.check_add(ctx, value.into(), true)?; 252 used_captures.extend(var_result.used_captures); 253 Ok(StatementResult { used_captures }) 254 } 255} 256 257impl ast::Assign { 258 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 259 let mut used_captures = HashSet::new(); 260 let value = self.value.check(ctx)?; 261 used_captures.extend(value.used_captures.iter().cloned()); 262 let var_result = self.variable.check_set(ctx, value.into())?; 263 used_captures.extend(var_result.used_captures); 264 Ok(StatementResult { used_captures }) 265 } 266} 267 268impl ast::CreateGraphNode { 269 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 270 let node_result = self.node.check_add( 271 ctx, 272 VariableResult { 273 is_local: true, 274 quantifier: One, 275 }, 276 false, 277 )?; 278 Ok(StatementResult { 279 used_captures: node_result.used_captures, 280 }) 281 } 282} 283 284impl ast::AddGraphNodeAttribute { 285 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 286 let mut used_captures = HashSet::new(); 287 let node_result = self.node.check(ctx)?; 288 used_captures.extend(node_result.used_captures); 289 for attribute in &mut self.attributes { 290 let attr_result = attribute.check(ctx)?; 291 used_captures.extend(attr_result.used_captures); 292 } 293 Ok(StatementResult { used_captures }) 294 } 295} 296 297impl ast::CreateEdge { 298 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 299 let mut used_captures = HashSet::new(); 300 let source_result = self.source.check(ctx)?; 301 used_captures.extend(source_result.used_captures); 302 let sink_result = self.sink.check(ctx)?; 303 used_captures.extend(sink_result.used_captures); 304 Ok(StatementResult { used_captures }) 305 } 306} 307 308impl ast::AddEdgeAttribute { 309 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 310 let mut used_captures = HashSet::new(); 311 let source_result = self.source.check(ctx)?; 312 used_captures.extend(source_result.used_captures); 313 let sink_result = self.sink.check(ctx)?; 314 used_captures.extend(sink_result.used_captures); 315 for attribute in &mut self.attributes { 316 let attr_result = attribute.check(ctx)?; 317 used_captures.extend(attr_result.used_captures); 318 } 319 Ok(StatementResult { used_captures }) 320 } 321} 322 323impl ast::Scan { 324 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 325 let mut used_captures = HashSet::new(); 326 327 let value_result = self.value.check(ctx)?; 328 if !value_result.is_local { 329 return Err(CheckError::ExpectedLocalValue(self.location)); 330 } 331 used_captures.extend(value_result.used_captures); 332 333 for arm in &mut self.arms { 334 // Be aware that this check is not complete, as it does not rule out 335 // all regular expressions that admit empty matches. For example, th 336 // regex "\b" matches empty strings within a larger non-empty one. 337 // Therefore, there is also a runtime check that checks that a match was 338 // non-empty. This is all to prevent non-termination of scan. 339 if let Some(_) = arm.regex.captures("") { 340 return Err(CheckError::NullableRegex( 341 arm.regex.to_string(), 342 arm.location, 343 )); 344 } 345 346 let mut arm_locals = VariableMap::nested(ctx.locals); 347 let mut arm_ctx = CheckContext { 348 globals: ctx.globals, 349 file_query: ctx.file_query, 350 stanza_index: ctx.stanza_index, 351 stanza_query: ctx.stanza_query, 352 locals: &mut arm_locals, 353 }; 354 355 for statement in &mut arm.statements { 356 let stmt_result = statement.check(&mut arm_ctx)?; 357 used_captures.extend(stmt_result.used_captures); 358 } 359 } 360 Ok(StatementResult { used_captures }) 361 } 362} 363 364impl ast::Print { 365 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 366 let mut used_captures = HashSet::new(); 367 for value in &mut self.values { 368 let value_result = value.check(ctx)?; 369 used_captures.extend(value_result.used_captures); 370 } 371 Ok(StatementResult { used_captures }) 372 } 373} 374 375impl ast::If { 376 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 377 let mut used_captures = HashSet::new(); 378 379 for arm in &mut self.arms { 380 for condition in &mut arm.conditions { 381 let condition_result = condition.check(ctx)?; 382 used_captures.extend(condition_result.used_captures); 383 } 384 385 let mut arm_locals = VariableMap::nested(ctx.locals); 386 let mut arm_ctx = CheckContext { 387 globals: ctx.globals, 388 file_query: ctx.file_query, 389 stanza_index: ctx.stanza_index, 390 stanza_query: ctx.stanza_query, 391 locals: &mut arm_locals, 392 }; 393 394 for statement in &mut arm.statements { 395 let stmt_result = statement.check(&mut arm_ctx)?; 396 used_captures.extend(stmt_result.used_captures); 397 } 398 } 399 Ok(StatementResult { used_captures }) 400 } 401} 402 403impl ast::Condition { 404 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 405 let mut used_captures = HashSet::new(); 406 match self { 407 Self::None { value, location } | Self::Some { value, location } => { 408 let value_result = value.check(ctx)?; 409 if !value_result.is_local { 410 return Err(CheckError::ExpectedLocalValue(*location)); 411 } 412 if value_result.quantifier != ZeroOrOne { 413 return Err(CheckError::ExpectedOptionalValue(*location)); 414 } 415 used_captures.extend(value_result.used_captures); 416 } 417 Self::Bool { value, location } => { 418 let value_result = value.check(ctx)?; 419 if !value_result.is_local { 420 return Err(CheckError::ExpectedLocalValue(*location)); 421 } 422 used_captures.extend(value_result.used_captures); 423 } 424 } 425 Ok(StatementResult { used_captures }) 426 } 427} 428 429impl ast::ForIn { 430 fn check(&mut self, ctx: &mut CheckContext) -> Result<StatementResult, CheckError> { 431 let mut used_captures = HashSet::new(); 432 433 let value_result = self.value.check(ctx)?; 434 if !value_result.is_local { 435 return Err(CheckError::ExpectedLocalValue(self.location)); 436 } 437 if value_result.quantifier != ZeroOrMore && value_result.quantifier != OneOrMore { 438 return Err(CheckError::ExpectedListValue(self.location)); 439 } 440 used_captures.extend(value_result.used_captures.iter().cloned()); 441 442 let mut loop_locals = VariableMap::nested(ctx.locals); 443 let mut loop_ctx = CheckContext { 444 globals: ctx.globals, 445 file_query: ctx.file_query, 446 stanza_index: ctx.stanza_index, 447 stanza_query: ctx.stanza_query, 448 locals: &mut loop_locals, 449 }; 450 let var_result = self 451 .variable 452 .check_add(&mut loop_ctx, value_result.into(), false)?; 453 used_captures.extend(var_result.used_captures); 454 455 for statement in &mut self.statements { 456 let stmt_result = statement.check(&mut loop_ctx)?; 457 used_captures.extend(stmt_result.used_captures); 458 } 459 460 Ok(StatementResult { used_captures }) 461 } 462} 463 464//----------------------------------------------------------------------------- 465// Expressions 466 467/// Expression checking result 468#[derive(Clone, Debug)] 469struct ExpressionResult { 470 is_local: bool, 471 quantifier: CaptureQuantifier, 472 used_captures: HashSet<Identifier>, 473} 474 475impl ast::Expression { 476 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 477 match self { 478 Self::FalseLiteral => Ok(ExpressionResult { 479 is_local: true, 480 quantifier: One, 481 used_captures: HashSet::default(), 482 }), 483 Self::NullLiteral => Ok(ExpressionResult { 484 is_local: true, 485 quantifier: One, 486 used_captures: HashSet::default(), 487 }), 488 Self::TrueLiteral => Ok(ExpressionResult { 489 is_local: true, 490 quantifier: One, 491 used_captures: HashSet::default(), 492 }), 493 Self::IntegerConstant(expr) => expr.check(ctx), 494 Self::StringConstant(expr) => expr.check(ctx), 495 Self::ListLiteral(expr) => expr.check(ctx), 496 Self::SetLiteral(expr) => expr.check(ctx), 497 Self::ListComprehension(expr) => expr.check(ctx), 498 Self::SetComprehension(expr) => expr.check(ctx), 499 Self::Capture(expr) => expr.check(ctx), 500 Self::Variable(expr) => expr.check_get(ctx), 501 Self::Call(expr) => expr.check(ctx), 502 Self::RegexCapture(expr) => expr.check(ctx), 503 } 504 } 505} 506 507impl ast::IntegerConstant { 508 fn check(&mut self, _ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 509 Ok(ExpressionResult { 510 is_local: true, 511 quantifier: One, 512 used_captures: HashSet::default(), 513 }) 514 } 515} 516 517impl ast::StringConstant { 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::ListLiteral { 528 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 529 let mut is_local = true; 530 let mut used_captures = HashSet::new(); 531 for element in &mut self.elements { 532 let element_result = element.check(ctx)?; 533 is_local &= element_result.is_local; 534 used_captures.extend(element_result.used_captures); 535 } 536 Ok(ExpressionResult { 537 is_local, 538 quantifier: ZeroOrMore, 539 used_captures, 540 }) 541 } 542} 543 544impl ast::SetLiteral { 545 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 546 let mut is_local = true; 547 let mut used_captures = HashSet::new(); 548 for element in &mut self.elements { 549 let element_result = element.check(ctx)?; 550 is_local &= element_result.is_local; 551 used_captures.extend(element_result.used_captures); 552 } 553 Ok(ExpressionResult { 554 is_local, 555 quantifier: ZeroOrMore, 556 used_captures, 557 }) 558 } 559} 560 561impl ast::ListComprehension { 562 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 563 let mut used_captures = HashSet::new(); 564 565 let value_result = self.value.check(ctx)?; 566 if !value_result.is_local { 567 return Err(CheckError::ExpectedLocalValue(self.location)); 568 } 569 if value_result.quantifier != ZeroOrMore && value_result.quantifier != OneOrMore { 570 return Err(CheckError::ExpectedListValue(self.location)); 571 } 572 used_captures.extend(value_result.used_captures.iter().cloned()); 573 574 let mut loop_locals = VariableMap::nested(ctx.locals); 575 let mut loop_ctx = CheckContext { 576 globals: ctx.globals, 577 file_query: ctx.file_query, 578 stanza_index: ctx.stanza_index, 579 stanza_query: ctx.stanza_query, 580 locals: &mut loop_locals, 581 }; 582 let var_result = self 583 .variable 584 .check_add(&mut loop_ctx, value_result.into(), false)?; 585 used_captures.extend(var_result.used_captures); 586 587 let element_result = self.element.check(&mut loop_ctx)?; 588 used_captures.extend(element_result.used_captures); 589 590 Ok(ExpressionResult { 591 is_local: element_result.is_local, 592 quantifier: ZeroOrMore, 593 used_captures, 594 }) 595 } 596} 597 598impl ast::SetComprehension { 599 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 600 let mut used_captures = HashSet::new(); 601 602 let value_result = self.value.check(ctx)?; 603 if !value_result.is_local { 604 return Err(CheckError::ExpectedLocalValue(self.location)); 605 } 606 if value_result.quantifier != ZeroOrMore && value_result.quantifier != OneOrMore { 607 return Err(CheckError::ExpectedListValue(self.location)); 608 } 609 used_captures.extend(value_result.used_captures.iter().cloned()); 610 611 let mut loop_locals = VariableMap::nested(ctx.locals); 612 let mut loop_ctx = CheckContext { 613 globals: ctx.globals, 614 file_query: ctx.file_query, 615 stanza_index: ctx.stanza_index, 616 stanza_query: ctx.stanza_query, 617 locals: &mut loop_locals, 618 }; 619 let var_result = self 620 .variable 621 .check_add(&mut loop_ctx, value_result.into(), false)?; 622 used_captures.extend(var_result.used_captures); 623 624 let element_result = self.element.check(&mut loop_ctx)?; 625 used_captures.extend(element_result.used_captures); 626 627 Ok(ExpressionResult { 628 is_local: element_result.is_local, 629 quantifier: ZeroOrMore, 630 used_captures, 631 }) 632 } 633} 634 635impl ast::Capture { 636 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 637 let name = self.name.to_string(); 638 self.stanza_capture_index = ctx 639 .stanza_query 640 .capture_index_for_name(&name) 641 .ok_or_else(|| CheckError::UndefinedSyntaxCapture(name.clone(), self.location))? 642 as usize; 643 self.file_capture_index = ctx 644 .file_query 645 .capture_index_for_name(&name) 646 .expect("missing capture index for name") as usize; // if the previous lookup succeeded, this one should succeed as well 647 self.quantifier = 648 ctx.file_query.capture_quantifiers(ctx.stanza_index)[self.file_capture_index]; 649 Ok(ExpressionResult { 650 is_local: true, 651 quantifier: self.quantifier, 652 used_captures: HashSet::from([self.name.clone()]), 653 }) 654 } 655} 656 657impl ast::Call { 658 fn check(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 659 let mut is_local = true; 660 let mut used_captures = HashSet::new(); 661 for parameter in &mut self.parameters { 662 let parameter_result = parameter.check(ctx)?; 663 is_local &= parameter_result.is_local; 664 used_captures.extend(parameter_result.used_captures); 665 } 666 Ok(ExpressionResult { 667 is_local, 668 quantifier: One, // FIXME we don't really know 669 used_captures, 670 }) 671 } 672} 673 674impl ast::RegexCapture { 675 fn check(&mut self, _ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 676 Ok(ExpressionResult { 677 is_local: true, 678 quantifier: One, 679 used_captures: HashSet::default(), 680 }) 681 } 682} 683 684//----------------------------------------------------------------------------- 685// Variables 686 687impl ast::Variable { 688 fn check_add( 689 &mut self, 690 ctx: &mut CheckContext, 691 value: VariableResult, 692 mutable: bool, 693 ) -> Result<StatementResult, CheckError> { 694 match self { 695 Self::Unscoped(v) => v.check_add(ctx, value, mutable), 696 Self::Scoped(v) => v.check_add(ctx, value, mutable), 697 } 698 } 699 700 fn check_set( 701 &mut self, 702 ctx: &mut CheckContext, 703 value: VariableResult, 704 ) -> Result<StatementResult, CheckError> { 705 match self { 706 Self::Unscoped(v) => v.check_set(ctx, value), 707 Self::Scoped(v) => v.check_set(ctx, value), 708 } 709 } 710 711 fn check_get(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 712 match self { 713 Self::Unscoped(v) => v.check_get(ctx), 714 Self::Scoped(v) => v.check_get(ctx), 715 } 716 } 717} 718 719impl ast::UnscopedVariable { 720 fn check_add( 721 &mut self, 722 ctx: &mut CheckContext, 723 value: VariableResult, 724 mutable: bool, 725 ) -> Result<StatementResult, CheckError> { 726 if ctx.globals.get(&self.name).is_some() { 727 return Err(CheckError::CannotHideGlobalVariable( 728 self.name.as_str().to_string(), 729 self.location, 730 )); 731 } 732 let mut value = value; 733 // Mutable variables are not considered local, because a non-local 734 // assignment in a loop could invalidate an earlier local assignment. 735 // Since we process all statement in order, we don't have info on later 736 // assignments, and can assume non-local to be sound. 737 if mutable { 738 value.is_local = false; 739 } 740 ctx.locals 741 .add(self.name.clone(), value, mutable) 742 .map_err(|e| CheckError::Variable(e, format!("{}", self.name), self.location))?; 743 Ok(StatementResult { 744 used_captures: HashSet::default(), 745 }) 746 } 747 748 fn check_set( 749 &mut self, 750 ctx: &mut CheckContext, 751 value: VariableResult, 752 ) -> Result<StatementResult, CheckError> { 753 if ctx.globals.get(&self.name).is_some() { 754 return Err(CheckError::CannotSetGlobalVariable( 755 self.name.as_str().to_string(), 756 self.location, 757 )); 758 } 759 let mut value = value; 760 // Mutable variables are not considered local, because a non-local 761 // assignment in a loop could invalidate an earlier local assignment. 762 // Since we process all statement in order, we don't have info on later 763 // assignments, and can assume non-local to be sound. 764 value.is_local = false; 765 ctx.locals 766 .set(self.name.clone(), value) 767 .map_err(|e| CheckError::Variable(e, format!("{}", self.name), self.location))?; 768 Ok(StatementResult { 769 used_captures: HashSet::default(), 770 }) 771 } 772 773 fn check_get(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 774 if let Some(result) = ctx.globals.get(&self.name) { 775 Some(result) 776 } else { 777 ctx.locals.get(&self.name) 778 } 779 .map(|value| value.into()) 780 .ok_or_else(|| CheckError::UndefinedVariable(self.name.as_str().to_string(), self.location)) 781 } 782} 783 784impl ast::ScopedVariable { 785 fn check_add( 786 &mut self, 787 ctx: &mut CheckContext, 788 _value: VariableResult, 789 _mutable: bool, 790 ) -> Result<StatementResult, CheckError> { 791 let scope_result = self.scope.check(ctx)?; 792 Ok(scope_result.into()) 793 } 794 795 fn check_set( 796 &mut self, 797 ctx: &mut CheckContext, 798 _value: VariableResult, 799 ) -> Result<StatementResult, CheckError> { 800 let scope_result = self.scope.check(ctx)?; 801 Ok(scope_result.into()) 802 } 803 804 fn check_get(&mut self, ctx: &mut CheckContext) -> Result<ExpressionResult, CheckError> { 805 let scope_result = self.scope.check(ctx)?; 806 Ok(ExpressionResult { 807 is_local: false, 808 quantifier: One, // FIXME we don't really know 809 used_captures: scope_result.used_captures, 810 }) 811 } 812} 813 814//----------------------------------------------------------------------------- 815// Attributes 816 817#[derive(Clone, Debug)] 818struct AttributeResult { 819 used_captures: HashSet<Identifier>, 820} 821 822impl ast::Attribute { 823 fn check(&mut self, ctx: &mut CheckContext) -> Result<AttributeResult, CheckError> { 824 let value_result = self.value.check(ctx)?; 825 Ok(AttributeResult { 826 used_captures: value_result.used_captures, 827 }) 828 } 829} 830 831//----------------------------------------------------------------------------- 832// Result Conversions 833 834impl Into<StatementResult> for ExpressionResult { 835 fn into(self) -> StatementResult { 836 StatementResult { 837 used_captures: self.used_captures, 838 } 839 } 840} 841 842impl Into<ExpressionResult> for &VariableResult { 843 fn into(self) -> ExpressionResult { 844 ExpressionResult { 845 is_local: self.is_local, 846 quantifier: self.quantifier, 847 used_captures: HashSet::default(), 848 } 849 } 850} 851 852impl Into<VariableResult> for ExpressionResult { 853 fn into(self) -> VariableResult { 854 VariableResult { 855 is_local: self.is_local, 856 quantifier: self.quantifier, 857 } 858 } 859}