diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 66b94797822..d29e1670944 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -370,7 +370,7 @@ pub struct FunctionDefinition { pub visibility: FunctionVisibility, pub generics: UnresolvedGenerics, - pub parameters: Vec<(Pattern, UnresolvedType, Visibility)>, + pub parameters: Vec, pub body: BlockExpression, pub span: Span, pub where_clause: Vec, @@ -379,6 +379,14 @@ pub struct FunctionDefinition { pub return_distinctness: Distinctness, } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Param { + pub visibility: Visibility, + pub pattern: Pattern, + pub typ: UnresolvedType, + pub span: Span, +} + #[derive(Debug, PartialEq, Eq, Clone)] pub enum FunctionReturnType { /// Returns type is not specified. @@ -634,8 +642,11 @@ impl FunctionDefinition { ) -> FunctionDefinition { let p = parameters .iter() - .map(|(ident, unresolved_type)| { - (Pattern::Identifier(ident.clone()), unresolved_type.clone(), Visibility::Private) + .map(|(ident, unresolved_type)| Param { + visibility: Visibility::Private, + pattern: Pattern::Identifier(ident.clone()), + typ: unresolved_type.clone(), + span: ident.span().merge(unresolved_type.span.unwrap()), }) .collect(); FunctionDefinition { @@ -661,8 +672,8 @@ impl Display for FunctionDefinition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{:?}", self.attributes)?; - let parameters = vecmap(&self.parameters, |(name, r#type, visibility)| { - format!("{name}: {visibility} {type}") + let parameters = vecmap(&self.parameters, |Param { visibility, pattern, typ, span: _ }| { + format!("{pattern}: {visibility} {typ}") }); let where_clause = vecmap(&self.where_clause, ToString::to_string); diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index e16c0fcba15..b8f385f52d3 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -4,7 +4,7 @@ use noirc_errors::Span; use crate::{ token::{Attributes, FunctionAttribute, SecondaryAttribute}, - FunctionReturnType, Ident, Pattern, Visibility, + FunctionReturnType, Ident, Param, Visibility, }; use super::{FunctionDefinition, UnresolvedType, UnresolvedTypeData}; @@ -45,6 +45,10 @@ impl NoirFunction { NoirFunction { kind: FunctionKind::Oracle, def } } + pub fn return_visibility(&self) -> Visibility { + self.def.return_visibility + } + pub fn return_type(&self) -> UnresolvedType { match &self.def.return_type { FunctionReturnType::Default(_) => { @@ -59,7 +63,7 @@ impl NoirFunction { pub fn name_ident(&self) -> &Ident { &self.def.name } - pub fn parameters(&self) -> &Vec<(Pattern, UnresolvedType, Visibility)> { + pub fn parameters(&self) -> &[Param] { &self.def.parameters } pub fn attributes(&self) -> &Attributes { diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 7d19a079203..1ca4d3101a9 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -436,6 +436,14 @@ pub enum Pattern { } impl Pattern { + pub fn span(&self) -> Span { + match self { + Pattern::Identifier(ident) => ident.span(), + Pattern::Mutable(_, span) | Pattern::Tuple(_, span) | Pattern::Struct(_, _, span) => { + *span + } + } + } pub fn name_ident(&self) -> &Ident { match self { Pattern::Identifier(name_ident) => name_ident, diff --git a/compiler/noirc_frontend/src/hir/aztec_library.rs b/compiler/noirc_frontend/src/hir/aztec_library.rs index b0e0e4f04c8..3b4703dc60f 100644 --- a/compiler/noirc_frontend/src/hir/aztec_library.rs +++ b/compiler/noirc_frontend/src/hir/aztec_library.rs @@ -17,7 +17,8 @@ use crate::{ }; use crate::{ ForLoopStatement, ForRange, FunctionDefinition, FunctionVisibility, ImportStatement, - NoirStruct, PrefixExpression, Signedness, StatementKind, StructType, Type, TypeImpl, UnaryOp, + NoirStruct, Param, PrefixExpression, Signedness, StatementKind, StructType, Type, TypeImpl, + UnaryOp, }; use fm::FileId; @@ -226,12 +227,12 @@ fn check_for_compute_note_hash_and_nullifier_definition(module: &SortedModule) - module.functions.iter().any(|func| { func.def.name.0.contents == "compute_note_hash_and_nullifier" && func.def.parameters.len() == 4 - && func.def.parameters[0].1.typ == UnresolvedTypeData::FieldElement - && func.def.parameters[1].1.typ == UnresolvedTypeData::FieldElement - && func.def.parameters[2].1.typ == UnresolvedTypeData::FieldElement + && func.def.parameters[0].typ.typ == UnresolvedTypeData::FieldElement + && func.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement + && func.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement // checks if the 4th parameter is an array and the Box in // Array(Option, Box) contains only fields - && match &func.def.parameters[3].1.typ { + && match &func.def.parameters[3].typ.typ { UnresolvedTypeData::Array(_, inner_type) => { match inner_type.typ { UnresolvedTypeData::FieldElement => true, @@ -513,14 +514,14 @@ fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { /// fn foo() { /// // ... /// } -pub(crate) fn create_inputs(ty: &str) -> (Pattern, UnresolvedType, Visibility) { +pub(crate) fn create_inputs(ty: &str) -> Param { let context_ident = ident("inputs"); let context_pattern = Pattern::Identifier(context_ident); let type_path = chained_path!("aztec", "abi", ty); let context_type = make_type(UnresolvedTypeData::Named(type_path, vec![])); let visibility = Visibility::Private; - (context_pattern, context_type, visibility) + Param { pattern: context_pattern, typ: context_type, visibility, span: Span::default() } } /// Creates the private context object to be accessed within the function, the parameters need to be extracted to be @@ -548,7 +549,7 @@ pub(crate) fn create_inputs(ty: &str) -> (Pattern, UnresolvedType, Visibility) { /// let mut context = PrivateContext::new(inputs, hasher.hash()); /// } /// ``` -fn create_context(ty: &str, params: &[(Pattern, UnresolvedType, Visibility)]) -> Vec { +fn create_context(ty: &str, params: &[Param]) -> Vec { let mut injected_expressions: Vec = vec![]; // `let mut hasher = Hasher::new();` @@ -564,7 +565,7 @@ fn create_context(ty: &str, params: &[(Pattern, UnresolvedType, Visibility)]) -> injected_expressions.push(let_hasher); // Iterate over each of the function parameters, adding to them to the hasher - params.iter().for_each(|(pattern, typ, _vis)| { + params.iter().for_each(|Param { pattern, typ, span: _, visibility: _ }| { match pattern { Pattern::Identifier(identifier) => { // Match the type to determine the padding to do diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index ce8545d2ccc..4b829932b76 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -38,7 +38,7 @@ use crate::{ }; use crate::{ ArrayLiteral, ContractFunctionType, Distinctness, ForRange, FunctionVisibility, Generics, - LValue, NoirStruct, NoirTypeAlias, Path, PathKind, Pattern, Shared, StructType, Type, + LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, Shared, StructType, Type, TypeAliasType, TypeBinding, TypeVariable, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT, @@ -760,7 +760,7 @@ impl<'a> Resolver<'a> { let mut parameters = vec![]; let mut parameter_types = vec![]; - for (pattern, typ, visibility) in func.parameters().iter().cloned() { + for Param { visibility, pattern, typ, span: _ } in func.parameters().iter().cloned() { if visibility == Visibility::Public && !self.pub_allowed(func) { self.push_err(ResolverError::UnnecessaryPub { ident: func.name_ident().clone(), diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index c46555e028c..09dc6dfff8d 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -97,6 +97,10 @@ impl ParserError { pub fn reason(&self) -> Option<&ParserErrorReason> { self.reason.as_ref() } + + pub fn is_warning(&self) -> bool { + matches!(self.reason(), Some(ParserErrorReason::ExperimentalFeature(_))) + } } impl std::fmt::Display for ParserError { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 57363bc117f..b8be0f91415 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -40,8 +40,8 @@ use crate::{ BinaryOp, BinaryOpKind, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, IfExpression, InfixExpression, LValue, Lambda, Literal, NoirFunction, NoirStruct, NoirTrait, - NoirTraitImpl, NoirTypeAlias, Path, PathKind, Pattern, Recoverable, Statement, TraitBound, - TraitImplItem, TraitItem, TypeImpl, UnaryOp, UnresolvedTraitConstraint, + NoirTraitImpl, NoirTypeAlias, Param, Path, PathKind, Pattern, Recoverable, Statement, + TraitBound, TraitImplItem, TraitItem, TypeImpl, UnaryOp, UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, }; @@ -342,9 +342,7 @@ fn lambda_parameters() -> impl NoirParser> { .labelled(ParsingRuleLabel::Parameter) } -fn function_parameters<'a>( - allow_self: bool, -) -> impl NoirParser> + 'a { +fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { let typ = parse_type().recover_via(parameter_recovery()); let full_parameter = pattern() @@ -352,7 +350,12 @@ fn function_parameters<'a>( .then_ignore(just(Token::Colon)) .then(optional_visibility()) .then(typ) - .map(|((name, visibility), typ)| (name, typ, visibility)); + .map_with_span(|((pattern, visibility), typ), span| Param { + visibility, + pattern, + typ, + span, + }); let self_parameter = if allow_self { self_parameter().boxed() } else { nothing().boxed() }; @@ -369,7 +372,7 @@ fn nothing() -> impl NoirParser { one_of([]).map(|_| unreachable!()) } -fn self_parameter() -> impl NoirParser<(Pattern, UnresolvedType, Visibility)> { +fn self_parameter() -> impl NoirParser { let mut_ref_pattern = just(Token::Ampersand).then_ignore(keyword(Keyword::Mut)); let mut_pattern = keyword(Keyword::Mut); @@ -398,7 +401,7 @@ fn self_parameter() -> impl NoirParser<(Pattern, UnresolvedType, Visibility)> { _ => (), } - (pattern, self_type, Visibility::Private) + Param { pattern, typ: self_type, visibility: Visibility::Private, span } }) } @@ -517,8 +520,8 @@ fn function_declaration_parameters() -> impl NoirParser (ident, param.1), + match param.pattern { + Pattern::Identifier(ident) => (ident, param.typ), other => { emit(ParserError::with_reason( ParserErrorReason::PatternInTraitFunctionParameter, @@ -526,7 +529,7 @@ fn function_declaration_parameters() -> impl NoirParser Result<(), CliEr let file_id = file_manager.add_file(&entry.path()).expect("file exists"); let (parsed_module, errors) = parse_file(&file_manager, file_id); - if !errors.is_empty() { + let is_all_warnings = errors.iter().all(ParserError::is_warning); + if !is_all_warnings { let errors = errors .into_iter() .map(|error| { diff --git a/tooling/nargo_fmt/build.rs b/tooling/nargo_fmt/build.rs index 3da07715e4e..cd93866ece9 100644 --- a/tooling/nargo_fmt/build.rs +++ b/tooling/nargo_fmt/build.rs @@ -59,7 +59,6 @@ fn format_{test_name}() {{ let (parsed_module, errors) = noirc_frontend::parse_program(&input); - assert!(errors.is_empty()); let config = nargo_fmt::Config::of("{config}").unwrap(); let fmt_text = nargo_fmt::format(&input, parsed_module, &config); diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index 0c15540037f..13938079e68 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -2,7 +2,7 @@ use crate::visitor::FmtVisitor; use noirc_frontend::hir::resolution::errors::Span; use noirc_frontend::lexer::Lexer; use noirc_frontend::token::Token; -use noirc_frontend::{Expression, Ident}; +use noirc_frontend::{Expression, Ident, Param, Visibility}; pub(crate) fn changed_comment_content(original: &str, new: &str) -> bool { comments(original).ne(comments(new)) @@ -237,6 +237,33 @@ impl Item for (Ident, Expression) { } } +impl Item for Param { + fn span(&self) -> Span { + self.span + } + + fn format(self, visitor: &FmtVisitor) -> String { + let visibility = match self.visibility { + Visibility::Public => "pub ", + Visibility::Private => "", + }; + let pattern = visitor.slice(self.pattern.span()); + let ty = visitor.slice(self.typ.span.unwrap()); + + format!("{pattern}: {visibility}{ty}") + } +} + +impl Item for Ident { + fn span(&self) -> Span { + self.span() + } + + fn format(self, visitor: &FmtVisitor) -> String { + visitor.slice(self.span()).into() + } +} + pub(crate) fn first_line_width(exprs: &str) -> usize { exprs.lines().next().map_or(0, |line: &str| line.chars().count()) } diff --git a/tooling/nargo_fmt/src/visitor/expr.rs b/tooling/nargo_fmt/src/visitor/expr.rs index 126508dbcf8..8492fd5c05d 100644 --- a/tooling/nargo_fmt/src/visitor/expr.rs +++ b/tooling/nargo_fmt/src/visitor/expr.rs @@ -365,7 +365,8 @@ impl FmtVisitor<'_> { } } -fn format_expr_seq( +#[allow(clippy::too_many_arguments)] +pub(crate) fn format_seq( prefix: &str, suffix: &str, mut visitor: FmtVisitor, @@ -373,6 +374,7 @@ fn format_expr_seq( exprs: Vec, span: Span, tactic: Tactic, + soft_newline: bool, ) -> String { visitor.indent.block_indent(visitor.config); @@ -382,7 +384,7 @@ fn format_expr_seq( visitor.indent.block_unindent(visitor.config); - wrap_exprs(prefix, suffix, exprs, nested_indent, visitor.shape(), false) + wrap_exprs(prefix, suffix, exprs, nested_indent, visitor.shape(), soft_newline) } fn format_brackets( @@ -392,7 +394,7 @@ fn format_brackets( span: Span, ) -> String { let array_width = visitor.config.array_width; - format_expr_seq( + format_seq( "[", "]", visitor, @@ -400,6 +402,7 @@ fn format_brackets( exprs, span, Tactic::LimitedHorizontalVertical(array_width), + false, ) } @@ -411,7 +414,7 @@ fn format_parens( span: Span, ) -> String { let tactic = max_width.map(Tactic::LimitedHorizontalVertical).unwrap_or(Tactic::Horizontal); - format_expr_seq("(", ")", visitor, trailing_comma, exprs, span, tactic) + format_seq("(", ")", visitor, trailing_comma, exprs, span, tactic, false) } fn format_exprs( @@ -503,12 +506,12 @@ pub(crate) fn wrap_exprs( exprs: String, nested_shape: Shape, shape: Shape, - array: bool, + soft_newline: bool, ) -> String { let first_line_width = first_line_width(&exprs); let force_one_line = - if array { !exprs.contains('\n') } else { first_line_width <= shape.width }; + if soft_newline { !exprs.contains('\n') } else { first_line_width <= shape.width }; if force_one_line { let allow_trailing_newline = exprs @@ -533,8 +536,8 @@ pub(crate) fn wrap_exprs( } } -#[derive(PartialEq, Eq)] -enum Tactic { +#[derive(PartialEq, Eq, Clone, Copy)] +pub(crate) enum Tactic { Horizontal, HorizontalVertical, LimitedHorizontalVertical(usize), diff --git a/tooling/nargo_fmt/src/visitor/item.rs b/tooling/nargo_fmt/src/visitor/item.rs index b1d3e274527..7774618afea 100644 --- a/tooling/nargo_fmt/src/visitor/item.rs +++ b/tooling/nargo_fmt/src/visitor/item.rs @@ -1,30 +1,81 @@ use noirc_frontend::{ parser::{Item, ItemKind}, token::Token, - NoirFunction, ParsedModule, + Distinctness, NoirFunction, ParsedModule, Visibility, }; -use crate::utils::last_line_contains_single_line_comment; +use crate::{utils::last_line_contains_single_line_comment, visitor::expr::format_seq}; + +use super::expr::Tactic::LimitedHorizontalVertical; impl super::FmtVisitor<'_> { fn format_fn_before_block(&self, func: NoirFunction, start: u32) -> (String, bool) { - let slice = self.slice(start..func.span().start()); + let tactic = LimitedHorizontalVertical(self.config.max_width); + + let name_span = func.name_ident().span(); + let func_span = func.span(); + + let mut result = self.slice(start..name_span.end()).to_owned(); - let params_open = self - .span_before(func.name_ident().span().end()..func.span().start(), Token::LeftParen) - .start(); + let params_open = + self.span_before(name_span.end()..func_span.start(), Token::LeftParen).start(); let last_span = if func.parameters().is_empty() { - params_open..func.span().start() + params_open..func_span.start() } else { - func.parameters().last().unwrap().1.span.unwrap().end()..func.span().start() + func.parameters().last().unwrap().span.end()..func_span.start() }; let params_end = self.span_after(last_span, Token::RightParen).start(); - let maybe_comment = self.slice(params_end..func.span().start()); + let params_span = params_open..params_end; + let return_type_span = func.return_type().span; + let parameters = func.def.parameters; + + if !func.def.generics.is_empty() { + let full_span = name_span.end()..params_open; + let start = name_span.end(); + let end = self.span_after(full_span, Token::Greater).start(); + + let generics = func.def.generics; + let span = (start..end).into(); + let generics = format_seq("<", ">", self.fork(), false, generics, span, tactic, false); + + result.push_str(&generics); + } + + let parameters = if parameters.is_empty() { + self.slice(params_span).into() + } else { + format_seq("(", ")", self.fork(), false, parameters, params_span.into(), tactic, true) + }; + + result.push_str(¶meters); + + if let Some(span) = return_type_span { + result.push_str(" -> "); + + if let Distinctness::Distinct = func.def.return_distinctness { + result.push_str("distinct "); + } + + if let Visibility::Public = func.def.return_visibility { + result.push_str("pub "); + } + + result.push_str(self.slice(span)); + + let slice = self.slice(span.end()..func_span.start()); + if !slice.trim().is_empty() { + result.push_str(slice); + } + } else { + result.push_str(self.slice(params_end..func_span.start())); + } + + let maybe_comment = self.slice(params_end..func_span.start()); - (slice.trim_end().to_string(), last_line_contains_single_line_comment(maybe_comment)) + (result.trim_end().to_string(), last_line_contains_single_line_comment(maybe_comment)) } pub(crate) fn visit_file(&mut self, module: ParsedModule) { diff --git a/tooling/nargo_fmt/tests/expected/fn.nr b/tooling/nargo_fmt/tests/expected/fn.nr index 61c2681fb20..5bca2dd8bb1 100644 --- a/tooling/nargo_fmt/tests/expected/fn.nr +++ b/tooling/nargo_fmt/tests/expected/fn.nr @@ -1,5 +1,9 @@ fn main(x: pub u8, y: u8) {} +fn main(x: pub u8, y: u8) -> pub Field {} + +fn main(x: A, y: B) -> pub Field where A: Eq, B: Eq {} + fn main() // hello {} @@ -12,3 +16,18 @@ fn main( // hello unit: () ) {} + +fn main() where T: Eq {} + +fn main( + tape: [Field; TAPE_LEN], + initial_registers: [Field; REGISTER_COUNT], + initial_memory: [Field; MEM_COUNT], + initial_program_counter: Field, + initial_call_stack: [Field; MAX_CALL_STACK], + initial_call_stack_pointer: u64 +) -> pub ExecutionResult {} + +fn apply_binary_field_op(lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, op: u8, registers: &mut Registers) -> bool {} + +fn main() -> distinct pub [Field;2] {} diff --git a/tooling/nargo_fmt/tests/expected/parens.nr b/tooling/nargo_fmt/tests/expected/parens.nr index aeba660f898..e6c4f91879c 100644 --- a/tooling/nargo_fmt/tests/expected/parens.nr +++ b/tooling/nargo_fmt/tests/expected/parens.nr @@ -1,4 +1,4 @@ -fn main(x : u64, y : pub u64) { +fn main(x: u64, y: pub u64) { ( // 1 diff --git a/tooling/nargo_fmt/tests/expected/struct.nr b/tooling/nargo_fmt/tests/expected/struct.nr index 87860ad9be9..6b80cc1d7d6 100644 --- a/tooling/nargo_fmt/tests/expected/struct.nr +++ b/tooling/nargo_fmt/tests/expected/struct.nr @@ -33,7 +33,7 @@ struct MyStruct { my_int: u32, my_nest: Nested, } -fn test_struct_in_tuple(a_bool : bool,x:Field, y:Field) -> (MyStruct, bool) { +fn test_struct_in_tuple(a_bool: bool, x: Field, y: Field) -> (MyStruct, bool) { let my_struct = MyStruct { my_bool: a_bool, my_int: 5, my_nest: Nested { a: x, b: y } }; (my_struct, a_bool) } diff --git a/tooling/nargo_fmt/tests/input/fn.nr b/tooling/nargo_fmt/tests/input/fn.nr index 61c2681fb20..f503db99853 100644 --- a/tooling/nargo_fmt/tests/input/fn.nr +++ b/tooling/nargo_fmt/tests/input/fn.nr @@ -1,5 +1,9 @@ fn main(x: pub u8, y: u8) {} +fn main(x: pub u8, y: u8) -> pub Field {} + +fn main(x: A, y: B) -> pub Field where A: Eq, B: Eq {} + fn main() // hello {} @@ -12,3 +16,11 @@ fn main( // hello unit: () ) {} + +fn main() where T: Eq {} + +fn main(tape: [Field; TAPE_LEN], initial_registers: [Field; REGISTER_COUNT], initial_memory: [Field; MEM_COUNT], initial_program_counter: Field, initial_call_stack: [Field; MAX_CALL_STACK], initial_call_stack_pointer: u64) -> pub ExecutionResult {} + +fn apply_binary_field_op(lhs: RegisterIndex, rhs: RegisterIndex, result: RegisterIndex, op: u8, registers: &mut Registers) -> bool {} + +fn main() -> distinct pub [Field;2] {}