@ -188,6 +188,58 @@ impl Program {
|
|||||||
Ok(x.clone())
|
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.
|
/// Returns the body item that includes the given character position.
|
||||||
pub fn get_body_item_for_position(&self, pos: usize) -> Option<&BodyItem> {
|
pub fn get_body_item_for_position(&self, pos: usize) -> Option<&BodyItem> {
|
||||||
for item in &self.body {
|
for item in &self.body {
|
||||||
@ -5183,4 +5235,40 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
|
|||||||
|
|
||||||
assert_eq!(l.raw, "false");
|
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'" }"#
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,6 +53,10 @@ impl Parser {
|
|||||||
return Ok(Program::default());
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user