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

Merge pull request #136 from tree-sitter/error-improvements

Error improvements and small bug fix

authored by Hendrik van Antwerpen and committed by GitHub ef6e334a a037d0c8

Changed files
+123 -44
src
tests
+12
CHANGELOG.md
··· 5 5 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 6 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 7 8 + ## v0.10.4 -- 2023-06-02 9 + 10 + ### Library 11 + 12 + #### Added 13 + 14 + - Several errors include more context in the error message: Duplicate errors report both statements using source snippets. Edge statements report which argument (the source or the sink) triggered an evluation error. 15 + 16 + #### Fixed 17 + 18 + - Ensure that edge attribute statements are executed after edges are created, to prevent non-deterministic ordering bugs in lazy execution mode. 19 + 8 20 ## v0.10.3 -- 2023-06-01 9 21 10 22 ### DSL
+1 -1
Cargo.toml
··· 1 1 [package] 2 2 name = "tree-sitter-graph" 3 - version = "0.10.3" 3 + version = "0.10.4" 4 4 description = "Construct graphs from parsed source code" 5 5 homepage = "https://github.com/tree-sitter/tree-sitter-graph/" 6 6 repository = "https://github.com/tree-sitter/tree-sitter-graph/"
+4 -6
src/execution/lazy.rs
··· 65 65 let mut locals = VariableMap::new(); 66 66 let mut store = LazyStore::new(); 67 67 let mut scoped_store = LazyScopedVariables::new(); 68 - let mut lazy_graph = Vec::new(); 68 + let mut lazy_graph = LazyGraph::new(); 69 69 let mut function_parameters = Vec::new(); 70 70 let mut prev_element_debug_info = HashMap::new(); 71 71 ··· 99 99 prev_element_debug_info: &mut prev_element_debug_info, 100 100 cancellation_flag, 101 101 }; 102 - for graph_stmt in &lazy_graph { 103 - graph_stmt.evaluate(&mut exec)?; 104 - } 102 + lazy_graph.evaluate(&mut exec)?; 105 103 // make sure any unforced values are now forced, to surface any problems 106 104 // hidden by the fact that the values were unused 107 105 store.evaluate_all(&mut exec)?; ··· 140 138 mat: &'a QueryMatch<'a, 'tree>, 141 139 store: &'a mut LazyStore, 142 140 scoped_store: &'a mut LazyScopedVariables, 143 - lazy_graph: &'a mut Vec<LazyStatement>, 141 + lazy_graph: &'a mut LazyGraph, 144 142 function_parameters: &'a mut Vec<graph::Value>, // re-usable buffer to reduce memory allocations 145 143 prev_element_debug_info: &'a mut HashMap<GraphElementKey, DebugInfo>, 146 144 error_context: StatementContext, ··· 179 177 locals: &mut VariableMap<'l, LazyValue>, 180 178 store: &mut LazyStore, 181 179 scoped_store: &mut LazyScopedVariables, 182 - lazy_graph: &mut Vec<LazyStatement>, 180 + lazy_graph: &mut LazyGraph, 183 181 function_parameters: &mut Vec<graph::Value>, 184 182 prev_element_debug_info: &mut HashMap<GraphElementKey, DebugInfo>, 185 183 inherited_variables: &HashSet<Identifier>,
+97 -33
src/execution/lazy/statements.rs
··· 22 22 use super::EvaluationContext; 23 23 use super::GraphElementKey; 24 24 25 + /// Lazy graph 26 + #[derive(Debug)] 27 + pub(super) struct LazyGraph { 28 + edge_statements: Vec<LazyStatement>, 29 + attr_statements: Vec<LazyStatement>, 30 + print_statements: Vec<LazyStatement>, 31 + } 32 + 33 + impl LazyGraph { 34 + pub(super) fn new() -> Self { 35 + LazyGraph { 36 + edge_statements: Vec::new(), 37 + attr_statements: Vec::new(), 38 + print_statements: Vec::new(), 39 + } 40 + } 41 + 42 + pub(super) fn push(&mut self, stmt: LazyStatement) { 43 + match stmt { 44 + LazyStatement::AddGraphNodeAttribute(_) => self.attr_statements.push(stmt), 45 + LazyStatement::CreateEdge(_) => self.edge_statements.push(stmt), 46 + LazyStatement::AddEdgeAttribute(_) => self.attr_statements.push(stmt), 47 + LazyStatement::Print(_) => self.print_statements.push(stmt), 48 + } 49 + } 50 + 51 + pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { 52 + for stmt in &self.edge_statements { 53 + stmt.evaluate(exec)?; 54 + } 55 + for stmt in &self.attr_statements { 56 + stmt.evaluate(exec)?; 57 + } 58 + for stmt in &self.print_statements { 59 + stmt.evaluate(exec)?; 60 + } 61 + Ok(()) 62 + } 63 + } 64 + 25 65 /// Lazy graph statements 26 66 #[derive(Debug)] 27 67 pub(super) enum LazyStatement { ··· 112 152 } 113 153 114 154 pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { 115 - let node = self.node.evaluate_as_graph_node(exec)?; 155 + let node = self 156 + .node 157 + .evaluate_as_graph_node(exec) 158 + .with_context(|| "Evaluating target node".to_string().into())?; 116 159 for attribute in &self.attributes { 117 160 let value = attribute.value.evaluate(exec)?; 118 161 let prev_debug_info = exec.prev_element_debug_info.insert( 119 162 GraphElementKey::NodeAttribute(node, attribute.name.clone()), 120 163 self.debug_info.clone(), 121 164 ); 122 - exec.graph[node] 165 + if let Err(_) = exec.graph[node] 123 166 .attributes 124 167 .add(attribute.name.clone(), value) 125 - .map_err(|_| { 126 - ExecutionError::DuplicateAttribute(format!( 127 - "{} on {} at {} and {}", 128 - attribute.name, 129 - node, 130 - prev_debug_info.unwrap(), 131 - self.debug_info, 132 - )) 133 - })?; 168 + { 169 + return Err(ExecutionError::DuplicateAttribute(format!( 170 + "{} on {}", 171 + attribute.name, node, 172 + ))) 173 + .with_context(|| { 174 + ( 175 + prev_debug_info.unwrap().into(), 176 + self.debug_info.clone().into(), 177 + ) 178 + .into() 179 + }); 180 + }; 134 181 } 135 182 Ok(()) 136 183 } ··· 171 218 } 172 219 173 220 pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { 174 - let source = self.source.evaluate_as_graph_node(exec)?; 175 - let sink = self.sink.evaluate_as_graph_node(exec)?; 221 + let source = self 222 + .source 223 + .evaluate_as_graph_node(exec) 224 + .with_context(|| "Evaluating edge source".to_string().into())?; 225 + let sink = self 226 + .sink 227 + .evaluate_as_graph_node(exec) 228 + .with_context(|| "Evaluating edge sink".to_string().into())?; 176 229 let prev_debug_info = exec 177 230 .prev_element_debug_info 178 231 .insert(GraphElementKey::Edge(source, sink), self.debug_info.clone()); ··· 180 233 Ok(edge) => edge, 181 234 Err(_) => { 182 235 return Err(ExecutionError::DuplicateEdge(format!( 183 - "({} -> {}) at {} and {}", 184 - source, 185 - sink, 186 - prev_debug_info.unwrap(), 187 - self.debug_info, 188 - )))? 236 + "({} -> {})", 237 + source, sink, 238 + ))) 239 + .with_context(|| { 240 + ( 241 + prev_debug_info.unwrap().into(), 242 + self.debug_info.clone().into(), 243 + ) 244 + .into() 245 + }); 189 246 } 190 247 }; 191 248 edge.attributes = self.attributes.clone(); ··· 228 285 } 229 286 230 287 pub(super) fn evaluate(&self, exec: &mut EvaluationContext) -> Result<(), ExecutionError> { 231 - let source = self.source.evaluate_as_graph_node(exec)?; 232 - let sink = self.sink.evaluate_as_graph_node(exec)?; 288 + let source = self 289 + .source 290 + .evaluate_as_graph_node(exec) 291 + .with_context(|| "Evaluating edge source".to_string().into())?; 292 + let sink = self 293 + .sink 294 + .evaluate_as_graph_node(exec) 295 + .with_context(|| "Evaluating edge sink".to_string().into())?; 233 296 for attribute in &self.attributes { 234 297 let value = attribute.value.evaluate(exec)?; 235 298 let edge = match exec.graph[source].get_edge_mut(sink) { ··· 243 306 GraphElementKey::EdgeAttribute(source, sink, attribute.name.clone()), 244 307 self.debug_info.clone(), 245 308 ); 246 - edge.attributes 247 - .add(attribute.name.clone(), value) 248 - .map_err(|_| { 249 - ExecutionError::DuplicateAttribute(format!( 250 - "{} on edge ({} -> {}) at {} and {}", 251 - attribute.name, 252 - source, 253 - sink, 254 - prev_debug_info.unwrap(), 255 - self.debug_info, 256 - )) 257 - })?; 309 + if let Err(_) = edge.attributes.add(attribute.name.clone(), value) { 310 + return Err(ExecutionError::DuplicateAttribute(format!( 311 + "{} on edge ({} -> {})", 312 + attribute.name, source, sink, 313 + ))) 314 + .with_context(|| { 315 + ( 316 + prev_debug_info.unwrap().into(), 317 + self.debug_info.clone().into(), 318 + ) 319 + .into() 320 + }); 321 + } 258 322 } 259 323 Ok(()) 260 324 }
+6 -1
src/execution/lazy/values.rs
··· 13 13 use std::fmt; 14 14 15 15 use crate::execution::error::ExecutionError; 16 + use crate::execution::error::ResultWithExecutionError; 16 17 use crate::graph::GraphNodeRef; 17 18 use crate::graph::SyntaxNodeRef; 18 19 use crate::graph::Value; ··· 184 185 } 185 186 186 187 fn resolve<'a>(&self, exec: &'a mut EvaluationContext) -> Result<LazyValue, ExecutionError> { 187 - let scope = self.scope.as_ref().evaluate_as_syntax_node(exec)?; 188 + let scope = self 189 + .scope 190 + .as_ref() 191 + .evaluate_as_syntax_node(exec) 192 + .with_context(|| format!("Evaluating scope of variable _.{}", self.name).into())?; 188 193 let scoped_store = &exec.scoped_store; 189 194 scoped_store.evaluate(&scope, &self.name, exec) 190 195 }
+3 -3
tests/it/lazy_execution.rs
··· 116 116 "#}, 117 117 indoc! {r#" 118 118 node 0 119 - name: "alpha" 120 - edge 0 -> 2 119 + edge 0 -> 1 121 120 node 1 122 - edge 1 -> 0 121 + name: "alpha" 122 + edge 1 -> 2 123 123 node 2 124 124 name: "beta" 125 125 edge 2 -> 3