From 6acaf5a9fa71cf02304728c801c410d75ad3152b Mon Sep 17 00:00:00 2001 From: Jess Frazelle Date: Mon, 24 Jun 2024 12:31:14 -0700 Subject: [PATCH] tests for dupes Signed-off-by: Jess Frazelle --- src/wasm-lib/kcl/src/ast/types.rs | 88 +++++++++++++++++++++++++++++++ src/wasm-lib/kcl/src/parser.rs | 6 ++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/wasm-lib/kcl/src/ast/types.rs b/src/wasm-lib/kcl/src/ast/types.rs index 206312f91..7abb4123f 100644 --- a/src/wasm-lib/kcl/src/ast/types.rs +++ b/src/wasm-lib/kcl/src/ast/types.rs @@ -188,6 +188,58 @@ impl Program { Ok(x.clone()) } + /// Validate no duplicate variables or tags. + pub fn validate_no_duplicate_variables_and_tags<'a>(&'a self) -> Result<(), KclError> { + let all: Arc>> = Arc::new(Mutex::new(HashMap::new())); + let found_dupe: Arc)>>> = Arc::new(Mutex::new(None)); + crate::lint::walk(self, &|node: crate::lint::Node<'a>| { + let mut findings = all.lock().map_err(|_| anyhow::anyhow!("mutex"))?; + let mut found = found_dupe.lock().map_err(|_| anyhow::anyhow!("mutex"))?; + match node { + crate::lint::Node::TagDeclarator(tag) => { + if let Some(existing) = findings.get(&tag.name) { + *found = Some((tag.name.clone(), vec![*existing, tag.into()])); + return Ok(true); + } + findings.insert(tag.name.to_string(), tag.into()); + } + + crate::lint::Node::VariableDeclaration(variables) => { + for variable in &variables.declarations { + if let Some(existing) = findings.get(&variable.id.name) { + *found = Some((variable.id.name.clone(), vec![*existing, variable.id.clone().into()])); + return Ok(true); + } + findings.insert(variable.id.name.to_string(), variable.into()); + } + } + _ => {} + } + Ok(true) + }) + .map_err(|err| { + KclError::Internal(KclErrorDetails { + source_ranges: vec![], + message: format!("Error while validating no duplicate variables and tags: {}", err), + }) + })?; + + let f = found_dupe.lock().map_err(|err| { + KclError::Internal(KclErrorDetails { + source_ranges: vec![], + message: format!("Error while validating no duplicate variables and tags: {}", err), + }) + })?; + if let Some((name, source_ranges)) = f.clone() { + return Err(KclError::Semantic(KclErrorDetails { + source_ranges, + message: format!("Duplicate variable or tag name: '{}'", name), + })); + } + + Ok(()) + } + /// Returns the body item that includes the given character position. pub fn get_body_item_for_position(&self, pos: usize) -> Option<&BodyItem> { for item in &self.body { @@ -5183,4 +5235,40 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#; assert_eq!(l.raw, "false"); } + + #[tokio::test(flavor = "multi_thread")] + async fn test_parse_duplicate_identifier_tag_variable_first() { + let some_program_string = r#"const thing = 134 +startSketchOn('xy') + |> startProfileAt([0,0], %) + |> line([10, 10], %, $thing)"#; + + let tokens = crate::token::lexer(some_program_string).unwrap(); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast(); + + assert!(program.is_err()); + assert_eq!( + program.err().unwrap().to_string(), + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([6, 17]), SourceRange([95, 101])], message: "Duplicate variable or tag name: 'thing'" }"# + ); + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_parse_duplicate_identifier_variable_tag_first() { + let some_program_string = r#"startSketchOn('xy') + |> startProfileAt([0,0], %) + |> line([10, 10], %, $thing) +const thing = 1"#; + + let tokens = crate::token::lexer(some_program_string).unwrap(); + let parser = crate::parser::Parser::new(tokens); + let program = parser.ast(); + + assert!(program.is_err()); + assert_eq!( + program.err().unwrap().to_string(), + r#"semantic: KclErrorDetails { source_ranges: [SourceRange([77, 83]), SourceRange([91, 96])], message: "Duplicate variable or tag name: 'thing'" }"# + ); + } } diff --git a/src/wasm-lib/kcl/src/parser.rs b/src/wasm-lib/kcl/src/parser.rs index 208b5e8d2..991e50bd8 100644 --- a/src/wasm-lib/kcl/src/parser.rs +++ b/src/wasm-lib/kcl/src/parser.rs @@ -53,6 +53,10 @@ impl Parser { return Ok(Program::default()); } - parser_impl::run_parser(&mut self.tokens.as_slice()) + let program = parser_impl::run_parser(&mut self.tokens.as_slice())?; + + program.validate_no_duplicate_variables_and_tags()?; + + Ok(program) } }