From 93d1fcc888e5cb86eacc8ad0f0d0702602bd6e79 Mon Sep 17 00:00:00 2001 From: mrRachar Date: Wed, 19 Aug 2020 17:30:50 +0100 Subject: [PATCH] Address #16 for contract and struct calls and for initialisation - Checks call arity and provided types, and produces more helpful error messages if not matched - New `scope_or_default()` on contexts will always produce a usable scope (replaces `.scope_context.as_ref().unwrap_or_default()`) - More conversions from wasteful `.collect()`s to plain `Iterators`, and optimisations to a more functional style --- Cargo.lock | 4 +- src/ast/declarations.rs | 31 +++-- src/ast/expressions.rs | 2 +- src/ast/mod.rs | 30 +++-- src/context.rs | 17 +++ src/environment/conflicts.rs | 2 +- src/environment/functions.rs | 86 +++++-------- src/environment/mod.rs | 48 +++++--- src/moveir/preprocessor/mod.rs | 6 +- src/moveir/preprocessor/utils.rs | 16 +-- src/moveir/statement.rs | 1 - src/semantic_analysis/mod.rs | 116 +++++++++++------- src/type_assigner/mod.rs | 2 +- src/type_checker/mod.rs | 2 +- src/utils/unique.rs | 4 +- tests/compilation_tests/call_check.flint | 1 + .../call_check_structs.flint | 38 ++++++ 17 files changed, 252 insertions(+), 154 deletions(-) create mode 100644 tests/compilation_tests/call_check_structs.flint diff --git a/Cargo.lock b/Cargo.lock index 5188588..630b6c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cc" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" +checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" [[package]] name = "cfg-if" diff --git a/src/ast/declarations.rs b/src/ast/declarations.rs index c523717..6da8630 100644 --- a/src/ast/declarations.rs +++ b/src/ast/declarations.rs @@ -3,7 +3,7 @@ use crate::ast_processor::Target; use crate::context::*; use crate::visitor::Visitor; use hex::encode; -use nom::lib::std::fmt::Formatter; +use std::fmt::Formatter; #[derive(Debug, Clone, PartialEq)] pub enum TopLevelDeclaration { @@ -641,30 +641,23 @@ impl FunctionSignatureDeclaration { self.modifiers.contains(&Modifier::Public) } - pub fn parameter_identifiers(&self) -> Vec { - self.parameters - .clone() - .into_iter() - .map(|p| p.identifier) - .collect() + pub fn parameter_identifiers<'a>(&'a self) -> impl Iterator + 'a { + self.parameters.iter().map(|p| &p.identifier) } - pub fn parameter_types(&self) -> Vec { - self.parameters - .clone() - .into_iter() - .map(|p| p.type_assignment) - .collect() + pub fn parameter_types<'a>(&'a self) -> impl Iterator + 'a { + self.parameters.iter().map(|p| &p.type_assignment) } pub fn is_equal(&self, against: FunctionSignatureDeclaration) -> bool { let modifiers_match = self.modifiers == against.modifiers; - let attibutes_match = self.attributes == against.attributes; - let parameter_names_match = self.parameter_identifiers() == against.parameter_identifiers(); - let parameter_types = self.parameter_types() == against.parameter_types(); + let attributes_match = self.attributes == against.attributes; + let parameter_names_match = self.parameter_identifiers() + .eq(against.parameter_identifiers()); + let parameter_types = self.parameter_types().eq(against.parameter_types()); if self.identifier.token == against.identifier.token && modifiers_match - && attibutes_match + && attributes_match && parameter_names_match && parameter_types { @@ -810,6 +803,10 @@ impl SpecialSignatureDeclaration { pub fn has_parameters(&self) -> bool { !self.parameters.is_empty() } + + pub fn parameter_types<'a>(&'a self) -> impl Iterator + 'a { + self.parameters.iter().map(|p| &p.type_assignment) + } } impl Visitable for SpecialSignatureDeclaration { diff --git a/src/ast/expressions.rs b/src/ast/expressions.rs index ab89172..2f3de21 100644 --- a/src/ast/expressions.rs +++ b/src/ast/expressions.rs @@ -283,7 +283,7 @@ impl Visitable for BinaryExpression { ctx.external_call_context = old_context; ctx.is_enclosing = false; - let scope = ctx.scope_context.as_ref().unwrap_or_default(); + let scope = ctx.scope_or_default(); let enclosing = ctx .enclosing_type_identifier() diff --git a/src/ast/mod.rs b/src/ast/mod.rs index cac743f..fda4012 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -214,6 +214,14 @@ impl SpecialInformation { .map(|p| p.type_assignment) .collect() } + + pub fn name(&self) -> &str { + &self.declaration.head.special_token + } + + pub fn get_parameter_types<'a>(&'a self) -> impl Iterator + 'a { + self.declaration.head.parameter_types() + } } #[derive(Default, Debug, Clone)] @@ -230,21 +238,27 @@ impl FunctionInformation { self.declaration.get_result_type() } - pub fn get_parameter_types(&self) -> Vec { + pub fn get_parameter_types<'a>(&'a self) -> impl Iterator + 'a { self.declaration.head.parameter_types() } - pub fn parameter_identifiers(&self) -> Vec { + pub fn parameter_identifiers<'a>(&'a self) -> impl Iterator + 'a { self.declaration.head.parameter_identifiers() } - pub fn required_parameter_identifiers(&self) -> Vec { - let identifiers = self.declaration.head.parameters.clone(); - identifiers - .into_iter() + pub fn required_parameter_identifiers(&self) -> impl Iterator { + self.declaration.head.parameters + .iter() .filter(|i| i.expression.is_none()) - .map(|p| p.identifier) - .collect() + .map(|p| &p.identifier) + } + + pub fn identifier(&self) -> &Identifier { + &self.declaration.head.identifier + } + + pub fn line_info(&self) -> &LineInfo { + &self.declaration.head.identifier.line_info } } diff --git a/src/context.rs b/src/context.rs index 55ff992..498fc93 100644 --- a/src/context.rs +++ b/src/context.rs @@ -74,6 +74,23 @@ impl Context { .map(|c| &*c.caller_protections) .unwrap_or(&[]) } + + pub fn scope_or_default(&self) -> &ScopeContext { + self.scope_context.as_ref().unwrap_or_default() + } + + pub fn declaration_context_type_id(&self) -> Option<&str> { + self.contract_behaviour_declaration_context.as_ref() + .map(|c| &*c.identifier.token) + .or_else( + || self.struct_declaration_context.as_ref() + .map(|c| &*c.identifier.token) + .or_else( + || self.asset_context.as_ref() + .map(|c| &*c.identifier.token) + ) + ) + } } #[derive(Debug)] diff --git a/src/environment/conflicts.rs b/src/environment/conflicts.rs index f64edd8..618191c 100644 --- a/src/environment/conflicts.rs +++ b/src/environment/conflicts.rs @@ -47,7 +47,7 @@ impl Environment { if let Some(first_signature) = funcs.get(0) { let first_parameter = &first_signature.declaration.head; let is_first_signature = |func: &FunctionInformation| { - func.get_parameter_types() == first_signature.get_parameter_types() + func.get_parameter_types().eq(first_signature.get_parameter_types()) && func.declaration.head.is_equal(first_parameter.clone()) }; if funcs.iter().any(is_first_signature) { diff --git a/src/environment/functions.rs b/src/environment/functions.rs index 01656f9..fabee0e 100644 --- a/src/environment/functions.rs +++ b/src/environment/functions.rs @@ -14,15 +14,8 @@ impl Environment { caller_protections: Vec, type_states: Vec, ) { - let name = f.head.identifier.token.clone(); + let name = &f.head.identifier.token; let mutating = f.is_mutating(); - let function_information = FunctionInformation { - declaration: f, - mutating, - caller_protections, - type_states, - ..Default::default() - }; let type_info = if let Some(type_info) = self.types.get_mut(type_id) { type_info } else { @@ -30,10 +23,19 @@ impl Environment { self.types.get_mut(type_id).unwrap() }; - if let Some(function_set) = type_info.functions.get_mut(&name) { - function_set.push(function_information); + // Allow further references without premature moving or code duplication + let function_information = |f| FunctionInformation { + declaration: f, + mutating, + caller_protections, + type_states, + ..Default::default() + }; + + if let Some(function_set) = type_info.functions.get_mut(name) { + function_set.push(function_information(f)); } else { - type_info.functions.insert(name, vec![function_information]); + type_info.functions.insert(name.clone(), vec![function_information(f)]); } } @@ -107,12 +109,6 @@ impl Environment { ) -> FunctionCallMatchResult { let mut candidates = Vec::new(); - let argument_types: Vec = call - .arguments - .iter() - .map(|a| self.get_expression_type(&a.expression, type_id, &[], &[], &scope)) - .collect(); - if let Some(type_info) = self.types.get(type_id) { if let Some(functions) = type_info.all_functions().get(&call.identifier.token) { for function in functions { @@ -136,22 +132,12 @@ impl Environment { } } + let argument_types: Vec<_> = self.argument_types(call, type_id, scope).collect(); + let matched_candidates: Vec = candidates .clone() .into_iter() - .filter(|c| { - let p_types = c.get_parameter_types(); - if p_types.len() != argument_types.len() { - return false; - } - let mut arg_types = argument_types.clone(); - for p in p_types { - if p != arg_types.remove(0) { - return false; - } - } - true - }) + .filter(|c| c.get_parameter_types().eq(argument_types.iter())) .collect(); let matched_candidates: Vec = matched_candidates @@ -204,12 +190,7 @@ impl Environment { if let Some(type_info) = self.types.get(&call.identifier.token) { for initialiser in &type_info.initialisers { let parameter_types = initialiser.parameter_types(); - let mut equal_types = true; - for argument_type in argument_types { - if !parameter_types.contains(argument_type) { - equal_types = false; - } - } + let equal_types = *¶meter_types == argument_types; if equal_types && self @@ -242,10 +223,10 @@ impl Environment { if let Some(type_info) = self.types.get(&"Quartz_Global".to_string()) { if let Some(functions) = type_info.functions.get(&call.identifier.token) { for function in functions { - let parameter_types = function.get_parameter_types(); + let parameter_types: Vec<_> = function.get_parameter_types().collect(); let equal_types = argument_types .iter() - .all(|argument_type| parameter_types.contains(argument_type)); + .all(|argument_type| parameter_types.contains(&argument_type)); if equal_types && self.compatible_caller_protections( protections, @@ -270,6 +251,12 @@ impl Environment { FunctionCallMatchResult::Failure(Candidates { candidates }) } + pub fn argument_types<'a>(&'a self, call: &'a FunctionCall, type_id: &'a str, scope: &'a ScopeContext) -> impl Iterator + 'a { + call.arguments + .iter() + .map(move |a| self.get_expression_type(&a.expression, type_id, &[], &[], scope)) + } + pub fn is_runtime_function_call(function_call: &FunctionCall) -> bool { function_call.identifier.token.starts_with("Quartz_") } @@ -281,9 +268,7 @@ impl Environment { caller_protections: &[CallerProtection], scope: &ScopeContext, ) -> FunctionCallMatchResult { - let result = FunctionCallMatchResult::Failure(Candidates { - ..Default::default() - }); + let result = FunctionCallMatchResult::Failure(Candidates { ..Default::default() }); let argument_types: Vec<_> = call .arguments @@ -298,9 +283,7 @@ impl Environment { let global_match = self.match_global_function(call, &argument_types, caller_protections); - let result = result.merge(regular_match); - let result = result.merge(initaliser_match); - result.merge(global_match) + result.merge(regular_match).merge(initaliser_match).merge(global_match) } fn compatible_caller_protections( @@ -337,8 +320,8 @@ impl Environment { type_id: &str, scope: &ScopeContext, ) -> bool { - let no_self_declaration_type = - Environment::replace_self(source.get_parameter_types(), type_id); + let no_self_declaration_type: Vec<_> = + Environment::replace_self(source.get_parameter_types(), type_id).collect(); let parameters: Vec<_> = source .declaration @@ -348,8 +331,8 @@ impl Environment { .map(|p| p.as_variable_declaration()) .collect(); - if target.arguments.len() <= source.parameter_identifiers().len() - && target.arguments.len() >= source.required_parameter_identifiers().len() + if target.arguments.len() <= source.parameter_identifiers().count() + && target.arguments.len() >= source.required_parameter_identifiers().count() { self.check_parameter_compatibility( &target.arguments, @@ -458,9 +441,8 @@ impl Environment { true } - pub fn replace_self(list: Vec, enclosing: &str) -> Vec { - list.into_iter() - .map(|t| t.replacing_self(enclosing)) - .collect() + pub fn replace_self<'a, I: 'a + Iterator>(iterator: I, enclosing: &'a str) -> impl Iterator + 'a { + iterator + .map(move |t| t.replacing_self(enclosing)) } } diff --git a/src/environment/mod.rs b/src/environment/mod.rs index 29d7e12..b872c92 100644 --- a/src/environment/mod.rs +++ b/src/environment/mod.rs @@ -55,6 +55,29 @@ pub enum CallableInformation { SpecialInformation(SpecialInformation), } +impl CallableInformation { + pub(crate) fn name(&self) -> &str { + match self { + CallableInformation::FunctionInformation(info) => &info.identifier().token, + CallableInformation::SpecialInformation(info) => info.name() + } + } + + pub(crate) fn line_info(&self) -> Option<&LineInfo> { + match self { + CallableInformation::FunctionInformation(info) => Some(info.line_info()), + _ => None + } + } + + pub(crate) fn get_parameter_types(&self) -> Vec<&Type> { + match self { + CallableInformation::FunctionInformation(info) => info.get_parameter_types().collect(), + CallableInformation::SpecialInformation(info) => info.get_parameter_types().collect() + } + } +} + #[derive(Debug, Default, Clone)] pub struct Candidates { pub(crate) candidates: Vec, @@ -198,22 +221,17 @@ impl Environment { _ => false, }; let caller_protection_function = |f: &FunctionInformation| { - if f.declaration.get_result_type().is_some() { - if f.get_result_type().unwrap().is_address_type() - && f.get_parameter_types().is_empty() - { - return true; + if let Some(result_type) = f.declaration.get_result_type() { + let mut parameter_types = f.get_parameter_types(); + if let Some(parameter_type) = parameter_types.next() { + // There are no further parameter types + parameter_types.all(|_| false) + && parameter_type.is_address_type() + && result_type.is_bool_type() + } else { + result_type.is_address_type() } - if f.get_result_type().unwrap().is_bool_type() && f.get_parameter_types().len() == 1 - { - let element = f.get_parameter_types().remove(0); - if element.is_address_type() { - return true; - } - } - return false; - } - false + } else { false } }; self.types .get(type_id) diff --git a/src/moveir/preprocessor/mod.rs b/src/moveir/preprocessor/mod.rs index 8ff1f20..bdbc546 100644 --- a/src/moveir/preprocessor/mod.rs +++ b/src/moveir/preprocessor/mod.rs @@ -927,7 +927,7 @@ impl Visitor for MovePreProcessor { &enclosing_type, &[], &[], - ctx.scope_context.as_ref().unwrap_or_default(), + ctx.scope_or_default(), ); if let Type::UserDefinedType(_) = expression_type { @@ -1055,7 +1055,7 @@ impl Visitor for MovePreProcessor { &enclosing_type, &[], &[], - ctx.scope_context.as_ref().unwrap_or_default(), + ctx.scope_or_default(), ) } } @@ -1064,7 +1064,7 @@ impl Visitor for MovePreProcessor { &enclosing_type, &[], &[], - ctx.scope_context.as_ref().unwrap_or_default(), + ctx.scope_or_default(), ), }; diff --git a/src/moveir/preprocessor/utils.rs b/src/moveir/preprocessor/utils.rs index a4b05b0..276b3fc 100644 --- a/src/moveir/preprocessor/utils.rs +++ b/src/moveir/preprocessor/utils.rs @@ -721,24 +721,24 @@ pub fn release(expression: Expression, expression_type: Type) -> Statement { })) } -pub fn mangle_function_call_name(function_call: &FunctionCall, ctx: &Context) -> Option { - if !Environment::is_runtime_function_call(function_call) && !ctx.is_external_function_call { +pub fn mangle_function_call_name(function_call: &FunctionCall, context: &Context) -> Option { + if !Environment::is_runtime_function_call(function_call) && !context.is_external_function_call { let enclosing_type = if let Some(ref enclosing) = function_call.identifier.enclosing_type { enclosing.clone() } else { - ctx.enclosing_type_identifier().unwrap().token.clone() + context.enclosing_type_identifier().unwrap().token.clone() }; let caller_protections: &[CallerProtection] = - if let Some(ref behaviour) = ctx.contract_behaviour_declaration_context { + if let Some(ref behaviour) = context.contract_behaviour_declaration_context { &behaviour.caller_protections } else { &[] }; - let scope = &ctx.scope_context.as_ref().unwrap_or_default(); + let scope = &context.scope_or_default(); - let match_result = ctx.environment.match_function_call( + let match_result = context.environment.match_function_call( &function_call, &enclosing_type, caller_protections, @@ -800,7 +800,7 @@ pub fn mangle_function_call_name(function_call: &FunctionCall, ctx: &Context) -> } } else { let _lol = !Environment::is_runtime_function_call(function_call); - let _lol2 = !ctx.is_external_function_call; + let _lol2 = !context.is_external_function_call; Some(function_call.identifier.token.clone()) } } @@ -814,7 +814,7 @@ pub fn is_global_function_call(function_call: &FunctionCall, ctx: &Context) -> b &[] }; - let scope = ctx.scope_context.as_ref().unwrap_or_default(); + let scope = ctx.scope_or_default(); let result = ctx.environment diff --git a/src/moveir/statement.rs b/src/moveir/statement.rs index 0e68975..88a88c5 100644 --- a/src/moveir/statement.rs +++ b/src/moveir/statement.rs @@ -60,7 +60,6 @@ impl MoveIfStatement { position: Default::default(), } .generate(function_context); - println!("With new block"); let count = function_context.push_block(); diff --git a/src/semantic_analysis/mod.rs b/src/semantic_analysis/mod.rs index 2b0abf4..2050b47 100644 --- a/src/semantic_analysis/mod.rs +++ b/src/semantic_analysis/mod.rs @@ -1,9 +1,11 @@ use super::ast::*; use super::context::*; use super::visitor::*; -use crate::environment::FunctionCallMatchResult::{MatchedFunction, MatchedInitializer}; +use crate::environment::FunctionCallMatchResult::{MatchedFunction, MatchedInitializer, Failure}; use crate::type_checker::ExpressionChecker; use crate::utils::unique::Unique; +use itertools::Itertools; +use crate::environment::Candidates; pub struct SemanticAnalysis {} @@ -71,7 +73,7 @@ impl Visitor for SemanticAnalysis { .map(|state| state.identifier.token.clone()) .collect::>() ) - .as_str(), + .as_str(), )); } @@ -102,7 +104,7 @@ impl Visitor for SemanticAnalysis { ) -> VResult { if context.environment.is_conflicting(&declaration.identifier) { let i = declaration.identifier.token.clone(); - return Err(Box::from(format!("Conflicting Declarations for {}", i))); + return Err(Box::from(format!("Conflicting declarations for {}", i))); } if context @@ -110,8 +112,9 @@ impl Visitor for SemanticAnalysis { .is_recursive_struct(&declaration.identifier.token) { return Err(Box::from(format!( - "Recusive Struct Definition for {}", - declaration.identifier.token + "Recusive struct definition for {} on {}", + declaration.identifier.token, + declaration.identifier.line_info ))); } @@ -123,7 +126,7 @@ impl Visitor for SemanticAnalysis { .environment .conflicting_trait_signatures(&declaration.identifier.token) { - return Err(Box::from("Conflicting Traits".to_owned())); + return Err(Box::from("Conflicting traits".to_owned())); } Ok(()) } @@ -269,7 +272,7 @@ impl Visitor for SemanticAnalysis { .parameters .iter() .map(|p| &p.identifier.token) - .unique() + .is_unique() { return Err(Box::from(format!( "Function {} has duplicate parameters", @@ -420,9 +423,9 @@ impl Visitor for SemanticAnalysis { .environment .is_contract_stateful(&context.identifier.token) && !declaration - .body - .iter() - .any(|state| matches!(state, Statement::BecomeStatement(_))) + .body + .iter() + .any(|state| matches!(state, Statement::BecomeStatement(_))) { return Err(Box::from( "Initialiser of a contract with typestates must have a `become` statement" @@ -562,7 +565,7 @@ impl Visitor for SemanticAnalysis { return if scope.is_declared(token) { Ok(()) } else if let Some(contract) = - &ctx.contract_behaviour_declaration_context + &ctx.contract_behaviour_declaration_context { if let Some(caller) = &contract.caller { if *token == caller.token { @@ -603,8 +606,7 @@ impl Visitor for SemanticAnalysis { let start = range_expression.start_expression.clone(); let end = range_expression.end_expression.clone(); - if is_literal(start.as_ref()) && is_literal(end.as_ref()) { - } else { + if is_literal(start.as_ref()) && is_literal(end.as_ref()) {} else { return Err(Box::from(format!( "Invalid Range Declaration: {:?}", range_expression @@ -622,9 +624,9 @@ impl Visitor for SemanticAnalysis { if context.enclosing_type_identifier().is_some() && !protection.is_any() && !context.environment.contains_caller_protection( - protection, - &context.enclosing_type_identifier().unwrap().token, - ) + protection, + &context.enclosing_type_identifier().unwrap().token, + ) { return Err(Box::from(format!( "Undeclared caller protection {}", @@ -691,7 +693,7 @@ impl Visitor for SemanticAnalysis { return Err(Box::from(format!( "Assignment to non-expression {:?}", expression.lhs_expression - ))) + ))); } } } else { @@ -730,34 +732,64 @@ impl Visitor for SemanticAnalysis { call: &mut FunctionCall, context: &mut crate::context::Context, ) -> VResult { + let fail = |candidates: Candidates, type_id: &str| if let Some(first) = candidates.candidates.first() { + Err( + Box::from(format!( + "Could not call `{}` with ({}) on {}, did you mean to call `{}` with ({}){}", + &call.identifier.token, + &context.environment.argument_types(&call, type_id, context.scope_or_default()).join(", "), + &call.identifier.line_info, + first.name(), + first.get_parameter_types().iter().join(", "), + first.line_info().map(|line| format!(" on {}", line)).as_deref().unwrap_or("") + )) + ) + } else { + Err( + Box::from(format!( + "Undefined function `{}` called on {}", + &call.identifier.token, + &call.identifier.line_info + )) + ) + }; + + let called_on_type = call.identifier.enclosing_type.as_deref().or_else( + || context.declaration_context_type_id() + ).unwrap_or_default(); if let Some(ref behaviour_context) = context.contract_behaviour_declaration_context { - let contract_name = &behaviour_context.identifier.token; + let contract_name = &*behaviour_context.identifier.token; + let contract_call = contract_name == called_on_type; let function_info = context.environment.match_function_call( &call, - contract_name, + called_on_type, &behaviour_context.caller_protections, - context.scope_context.as_ref().unwrap_or_default(), + context.scope_or_default(), ); - return match function_info { - MatchedFunction(info) => check_if_correct_type_state_possible( + + match function_info { + MatchedFunction(info) if contract_call => check_if_correct_type_state_possible( behaviour_context, context.environment.get_contract_state(contract_name), - info.type_states, - call.identifier.clone(), + &info.type_states, + &call.identifier, ), - MatchedInitializer(info) => check_if_correct_type_state_possible( + MatchedInitializer(info) if contract_call => check_if_correct_type_state_possible( behaviour_context, context.environment.get_contract_state(contract_name), - info.type_states, - call.identifier.clone(), + &info.type_states, + &call.identifier, ), - // Otherwise we are not calling a contract method so type states do not apply - _ => Ok(()), - }; + Failure(candidates) => fail(candidates, contract_name), + _ => Ok(()) + } + } else { + match context.environment.match_function_call(&call, called_on_type, &[], context.scope_or_default()) { + Failure(candidates) => fail(candidates, context.declaration_context_type_id().unwrap_or_default()), + _ => Ok(()) + } } - - Ok(()) } #[allow(clippy::single_match)] @@ -859,7 +891,7 @@ impl Visitor for SemanticAnalysis { enclosing_type, &type_states, &caller_protections, - context.scope_context.as_ref().unwrap_or_default(), + context.scope_or_default(), ) { Ok(()) } else { @@ -874,8 +906,8 @@ impl Visitor for SemanticAnalysis { fn check_if_correct_type_state_possible( declaration_context: &crate::context::ContractBehaviourDeclarationContext, current_state: Option, - allowed_states: Vec, - function_id: Identifier, + allowed_states: &[TypeState], + function_id: &Identifier, ) -> VResult { let current_possible_states = if let Some(state) = current_state { vec![state] @@ -887,8 +919,8 @@ fn check_if_correct_type_state_possible( if allowed_states.is_empty() || current_possible_states.is_empty() || current_possible_states - .iter() - .all(|state| allowed_states.contains(state)) + .iter() + .all(|state| allowed_states.contains(state)) { Ok(()) } else { @@ -905,11 +937,11 @@ fn check_if_correct_type_state_possible( } } -fn is_conformance_repeated<'a, T: IntoIterator>(conformances: T) -> bool { +fn is_conformance_repeated<'a, T: IntoIterator>(conformances: T) -> bool { !conformances .into_iter() .map(|c| &c.identifier.token) - .unique() + .is_unique() } fn code_block_returns(block: &[Statement]) -> bool { @@ -928,9 +960,9 @@ fn code_block_returns(block: &[Statement]) -> bool { .iter() .any(|statements| matches!(statements, Statement::ReturnStatement(_))) || (branches.peek().is_some() - && branches.all(|branch| { - code_block_returns(&branch.body) && code_block_returns(&branch.else_body) - })) + && branches.all(|branch| { + code_block_returns(&branch.body) && code_block_returns(&branch.else_body) + })) } fn ensure_mutation_declared(token: &str, ctx: &Context) -> VResult { diff --git a/src/type_assigner/mod.rs b/src/type_assigner/mod.rs index a20560a..d6427fb 100644 --- a/src/type_assigner/mod.rs +++ b/src/type_assigner/mod.rs @@ -37,7 +37,7 @@ impl Visitor for TypeAssigner { if let BinOp::Dot = bin_expr.op { let enclosing = ctx.enclosing_type_identifier(); let enclosing = enclosing.unwrap(); - let scope = &ctx.scope_context.as_ref().unwrap_or_default(); + let scope = &ctx.scope_or_default(); let lhs_type = ctx.environment.get_expression_type( &*bin_expr.lhs_expression, &enclosing.token, diff --git a/src/type_checker/mod.rs b/src/type_checker/mod.rs index 251d5e4..fef841a 100644 --- a/src/type_checker/mod.rs +++ b/src/type_checker/mod.rs @@ -78,7 +78,7 @@ impl Visitor for TypeChecker { enclosing, &[], &[], - _ctx.scope_context.as_ref().unwrap_or_default(), + _ctx.scope_or_default(), ); match _t.op { BinOp::Dot => _t.rhs_expression.assign_enclosing_type(&lhs_type.name()), diff --git a/src/utils/unique.rs b/src/utils/unique.rs index 3069c15..75765e0 100644 --- a/src/utils/unique.rs +++ b/src/utils/unique.rs @@ -1,11 +1,11 @@ use std::collections::HashSet; pub trait Unique { - fn unique(self) -> bool; + fn is_unique(self) -> bool; } impl, V: std::cmp::Eq + std::hash::Hash> Unique for T { - fn unique(self) -> bool { + fn is_unique(self) -> bool { let mut seen: HashSet<_> = HashSet::new(); self.into_iter().all(move |i| seen.insert(i)) } diff --git a/tests/compilation_tests/call_check.flint b/tests/compilation_tests/call_check.flint index 62c769e..70423f2 100644 --- a/tests/compilation_tests/call_check.flint +++ b/tests/compilation_tests/call_check.flint @@ -2,6 +2,7 @@ contract Test { } Test :: (any) { public init() { + //! compile fail call `test0` with (Bool) on line 6, did you mean to call `test0` with () on line 19 test0(true) test1() diff --git a/tests/compilation_tests/call_check_structs.flint b/tests/compilation_tests/call_check_structs.flint new file mode 100644 index 0000000..d89c20e --- /dev/null +++ b/tests/compilation_tests/call_check_structs.flint @@ -0,0 +1,38 @@ +contract Test { } + +struct Struct { + public init(x: Int) { } + + public func tests0() -> Int { + return 0 + } + + public func tests1(x: Bool) -> Int { + return 1 + } + + public func tests2(x: Int, y: Int) -> Int { + return 2 + } +} + +Test :: (any) { + public init() { + //var s0: Struct = Struct() + var s: Struct = Struct(1) + //var s1: Struct = Struct(1, 2) + + //! compile fail call `tests0` with (Bool) on line 25, did you mean to call `tests0` with () on line 6 + s.tests0(true) + s.tests0() + + s.tests1() + s.tests1(1) + s.tests1(true, 2) + + s.tests2() + s.tests2(1) + s.tests2(1, true) + s.tests2(1, 2, 3) + } +} \ No newline at end of file