diff --git a/src/ast.rs b/src/ast.rs index 88c38ad2..aa94e4a8 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1742,15 +1742,28 @@ impl AbstractSyntaxTree for Function { ); if from.name() != MAIN_STR { + let mut seen_params = HashSet::new(); + // A broken type in the signature becomes `!`, so the function is still // registered and its calls do not report it as undefined. let params = from .params() .iter() - .map(|param| FunctionParam { - identifier: param.identifier().clone(), - ty: scope.resolve_or_never(param.ty(), from), - span: *param.span(), + .map(|param| { + if !seen_params.insert(param.identifier()) { + scope.report( + Error::ParameterRedefined { + identifier: param.identifier().clone(), + } + .with_span(*param.span()), + ); + } + + FunctionParam { + identifier: param.identifier().clone(), + ty: scope.resolve_or_never(param.ty(), from), + span: *param.span(), + } }) .collect::>(); let ret = from @@ -2911,6 +2924,17 @@ impl AsRef for EnumMatchArm { } } +#[cfg(test)] +fn analyze(src: &str) -> Result<(), String> { + crate::TemplateAst::new_with_unstable( + src, + &crate::UnstableFeatures::all(), + Box::new(ElementsJetHinter::new()), + ) + .map(|_| ()) + .map_err(|e| e.to_string()) +} + #[cfg(test)] mod span_tests { use crate::parse::ParseFromStr; @@ -3498,9 +3522,10 @@ mod scope_balance_tests { #[cfg(test)] mod scope_resolution_tests { - use super::{ElementsJetHinter, Program}; use crate::driver::tests::setup_graph; + use super::{analyze, ElementsJetHinter, Program}; + pub(super) fn analyze_multifile(files: Vec<(&str, &str)>) -> Result<(), String> { let (graph, _ids, _dir, mut diagnostics) = setup_graph(files); @@ -3523,6 +3548,22 @@ mod scope_resolution_tests { } } + #[test] + fn duplicate_function_parameter_is_rejected() { + let err = analyze( + "fn f(a: u256, a: Pubkey) -> u256 { a } + fn main() { + let _: u256 = f(0, 1); + }", + ) + .unwrap_err(); + + assert!( + err.contains("Parameter `a` was defined multiple times"), + "{err}" + ); + } + #[test] fn private_type_alias_from_dependency_does_not_leak() { let result = analyze_multifile(vec![ @@ -4019,15 +4060,7 @@ mod enum_tests { use crate::ast::ElementsJetHinter; use crate::{TemplateAst, UnstableFeatures}; - fn analyze(src: &str) -> Result<(), String> { - TemplateAst::new_with_unstable( - src, - &UnstableFeatures::all(), - Box::new(ElementsJetHinter::new()), - ) - .map(|_| ()) - .map_err(|e| e.to_string()) - } + use super::analyze; #[test] fn enum_declaration_registers_type_alias() { diff --git a/src/error.rs b/src/error.rs index 90f00bed..fa2931ab 100644 --- a/src/error.rs +++ b/src/error.rs @@ -890,6 +890,9 @@ pub enum Error { MainOutOfEntryFile, MainCannotBePublic, MainCannotBeAlias, + ParameterRedefined { + identifier: Identifier, + }, FunctionRedefined { name: FunctionName, }, @@ -1120,6 +1123,10 @@ impl fmt::Display for Error { f, "Main function cannot be alias", ), + Error::ParameterRedefined { identifier } => write!( + f, + "Parameter `{identifier}` was defined multiple times" + ), Error::FunctionRedefined { name } => write!( f, "Function `{name}` was defined multiple times"