tests for dupes

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2024-06-24 12:31:14 -07:00
parent 688ccbf2d9
commit 6acaf5a9fa
2 changed files with 93 additions and 1 deletions

View File

@ -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<Mutex<HashMap<String, SourceRange>>> = Arc::new(Mutex::new(HashMap::new()));
let found_dupe: Arc<Mutex<Option<(String, Vec<SourceRange>)>>> = 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'" }"#
);
}
}

View File

@ -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)
}
}