diff --git a/.noir-sync-commit b/.noir-sync-commit index cd2891c1558..336e1ccacbf 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -e2f7e950c44883228d5e1230b04c83e479de7ed0 +cdbb940a883ae32dd84c667ec06b0d155f2d7520 diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 63509170057..7d9b3254c53 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -50,6 +50,12 @@ rust-version = "1.74.1" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" +[workspace.lints.rust] +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_import_braces = "warn" +unused_qualifications = "warn" + [workspace.dependencies] # ACVM workspace dependencies diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index 88616ccffb5..860b565544b 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs index 00d0933a3aa..43984e4a922 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -103,7 +103,7 @@ impl ErrorSelector { impl Serialize for ErrorSelector { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { self.0.to_string().serialize(serializer) } @@ -112,7 +112,7 @@ impl Serialize for ErrorSelector { impl<'de> Deserialize<'de> for ErrorSelector { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { let s: String = Deserialize::deserialize(deserializer)?; let as_u64 = s.parse().map_err(serde::de::Error::custom)?; @@ -224,7 +224,7 @@ impl Circuit { } impl Program { - fn write(&self, writer: W) -> std::io::Result<()> { + fn write(&self, writer: W) -> std::io::Result<()> { let buf = bincode::serialize(self).unwrap(); let mut encoder = flate2::write::GzEncoder::new(writer, Compression::default()); encoder.write_all(&buf)?; @@ -250,7 +250,7 @@ impl Program { } impl Deserialize<'a>> Program { - fn read(reader: R) -> std::io::Result { + fn read(reader: R) -> std::io::Result { let mut gz_decoder = flate2::read::GzDecoder::new(reader); let mut buf_d = Vec::new(); gz_decoder.read_to_end(&mut buf_d)?; diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index a037a453348..c1cffc1334e 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs index 92ccbc4e5f6..2323f008dbe 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/field_element.rs @@ -115,7 +115,7 @@ impl From for FieldElement { } } -impl Serialize for FieldElement { +impl Serialize for FieldElement { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -124,7 +124,7 @@ impl Serialize for FieldElement { } } -impl<'de, T: ark_ff::PrimeField> Deserialize<'de> for FieldElement { +impl<'de, T: PrimeField> Deserialize<'de> for FieldElement { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, diff --git a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs index f7228e66314..74927c07a36 100644 --- a/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs +++ b/noir/noir-repo/acvm-repo/acir_field/src/generic_ark.rs @@ -2,7 +2,7 @@ use num_bigint::BigUint; /// This trait is extremely unstable and WILL have breaking changes. pub trait AcirField: - std::marker::Sized + Sized + std::fmt::Display + std::fmt::Debug + Default @@ -24,7 +24,7 @@ pub trait AcirField: // + From + From + std::hash::Hash - + std::cmp::Eq + + Eq { fn one() -> Self; fn zero() -> Self; diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 2ee4d529e5a..bf1170ce073 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml index 0d90b9ba54f..6b457a8f5f8 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm_js/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] diff --git a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs index 23da88247fc..f6a00af7942 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs +++ b/noir/noir-repo/acvm-repo/acvm_js/src/js_execution_error.rs @@ -54,16 +54,14 @@ impl JsExecutionError { None => JsValue::UNDEFINED, }; let assertion_payload = match assertion_payload { - Some(raw) => ::from_serde(&raw) + Some(raw) => ::from_serde(&raw) .expect("Cannot serialize assertion payload"), None => JsValue::UNDEFINED, }; let brillig_function_id = match brillig_function_id { - Some(function_id) => { - ::from_serde(&function_id) - .expect("Cannot serialize Brillig function id") - } + Some(function_id) => ::from_serde(&function_id) + .expect("Cannot serialize Brillig function id"), None => JsValue::UNDEFINED, }; diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml index 10b491c7f67..d0473857e1c 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml index ab677396c22..9a23f503a06 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs index bb51426b33b..a4125014d56 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs @@ -91,7 +91,7 @@ mod test { fn test_derive_generators() { let res = derive_generators("test domain".as_bytes(), 128, 0); - let is_unique = |y: Affine, j: usize| -> bool { + let is_unique = |y: Affine, j: usize| -> bool { for (i, res) in res.iter().enumerate() { if i != j && *res == y { return false; diff --git a/noir/noir-repo/acvm-repo/brillig/Cargo.toml b/noir/noir-repo/acvm-repo/brillig/Cargo.toml index 631acbd55d8..9f5a5ce0ee2 100644 --- a/noir/noir-repo/acvm-repo/brillig/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml index ebd35f1579f..cd9f754ebc0 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/brillig_vm/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index a99a654aeed..c9d88e36e28 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -7,6 +7,9 @@ rust-version.workspace = true license.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/fm/Cargo.toml b/noir/noir-repo/compiler/fm/Cargo.toml index b48f445be36..c367333a6f4 100644 --- a/noir/noir-repo/compiler/fm/Cargo.toml +++ b/noir/noir-repo/compiler/fm/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_arena/Cargo.toml b/noir/noir-repo/compiler/noirc_arena/Cargo.toml index b94f997b7b9..68fc4d1c920 100644 --- a/noir/noir-repo/compiler/noirc_arena/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_arena/Cargo.toml @@ -5,3 +5,6 @@ authors.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true + +[lints] +workspace = true \ No newline at end of file diff --git a/noir/noir-repo/compiler/noirc_driver/Cargo.toml b/noir/noir-repo/compiler/noirc_driver/Cargo.toml index a9949f5093a..b244018cc71 100644 --- a/noir/noir-repo/compiler/noirc_driver/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_driver/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index b28606d3739..467bda2ca88 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -274,11 +274,8 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let macros: &[&dyn MacroProcessor] = if options.disable_macros { - &[] - } else { - &[&aztec_macros::AztecMacro as &dyn MacroProcessor] - }; + let macros: &[&dyn MacroProcessor] = + if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro] }; let mut errors = vec![]; let diagnostics = CrateDefMap::collect_defs( diff --git a/noir/noir-repo/compiler/noirc_errors/Cargo.toml b/noir/noir-repo/compiler/noirc_errors/Cargo.toml index 41b1cd0ff58..61b274c605f 100644 --- a/noir/noir-repo/compiler/noirc_errors/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_errors/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index 02b242e8b4d..1792197eab7 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -16,7 +16,7 @@ pub struct Spanned { /// This is important for tests. Two Spanned objects are equal if their content is equal /// They may not have the same span. Use into_span to test for Span being equal specifically -impl PartialEq> for Spanned { +impl PartialEq> for Spanned { fn eq(&self, other: &Spanned) -> bool { self.contents == other.contents } diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 66c770b5064..81feb0b7154 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index b2a73106468..24b30b22ec4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -2005,7 +2005,7 @@ impl PartialEq for AcirVarData { } // TODO: check/test this hash impl -impl std::hash::Hash for AcirVarData { +impl Hash for AcirVarData { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 9f11267f100..e7465f62ece 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -235,7 +235,7 @@ impl Debug for AcirDynamicArray { #[derive(Debug, Clone)] pub(crate) enum AcirValue { Var(AcirVar, AcirType), - Array(im::Vector), + Array(Vector), DynamicArray(AcirDynamicArray), } @@ -1650,7 +1650,7 @@ impl<'a> Context<'a> { let read = self.acir_context.read_from_memory(source, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) })?; - let array: im::Vector = init_values.into(); + let array: Vector = init_values.into(); self.initialize_array(destination, array_len, Some(AcirValue::Array(array)))?; Ok(()) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 34d7d595eb9..f06f46d7af8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -171,13 +171,14 @@ impl DataFlowGraph { ctrl_typevars: Option>, call_stack: CallStack, ) -> InsertInstructionResult { - use InsertInstructionResult::*; match instruction.simplify(self, block, ctrl_typevars.clone(), &call_stack) { - SimplifyResult::SimplifiedTo(simplification) => SimplifiedTo(simplification), + SimplifyResult::SimplifiedTo(simplification) => { + InsertInstructionResult::SimplifiedTo(simplification) + } SimplifyResult::SimplifiedToMultiple(simplification) => { - SimplifiedToMultiple(simplification) + InsertInstructionResult::SimplifiedToMultiple(simplification) } - SimplifyResult::Remove => InstructionRemoved, + SimplifyResult::Remove => InsertInstructionResult::InstructionRemoved, result @ (SimplifyResult::SimplifiedToInstruction(_) | SimplifyResult::SimplifiedToInstructionMultiple(_) | SimplifyResult::None) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs index 1c9a31f0c99..f1265553b83 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs @@ -50,7 +50,7 @@ impl Id { // Need to manually implement most impls on Id. // Otherwise rust assumes that Id: Hash only if T: Hash, // which isn't true since the T is not used internally. -impl std::hash::Hash for Id { +impl Hash for Id { fn hash(&self, state: &mut H) { self.index.hash(state); } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 084584beab3..13e5c2445ad 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -85,7 +85,7 @@ pub(super) struct Loop { } /// The queue of functions remaining to compile -type FunctionQueue = Vec<(ast::FuncId, IrFunctionId)>; +type FunctionQueue = Vec<(FuncId, IrFunctionId)>; impl<'a> FunctionContext<'a> { /// Create a new FunctionContext to compile the first function in the shared_context's @@ -1005,14 +1005,14 @@ impl SharedContext { } /// Pops the next function from the shared function queue, returning None if the queue is empty. - pub(super) fn pop_next_function_in_queue(&self) -> Option<(ast::FuncId, IrFunctionId)> { + pub(super) fn pop_next_function_in_queue(&self) -> Option<(FuncId, IrFunctionId)> { self.function_queue.lock().expect("Failed to lock function_queue").pop() } /// Return the matching id for the given function if known. If it is not known this /// will add the function to the queue of functions to compile, assign it a new id, /// and return this new id. - pub(super) fn get_or_queue_function(&self, id: ast::FuncId) -> IrFunctionId { + pub(super) fn get_or_queue_function(&self, id: FuncId) -> IrFunctionId { // Start a new block to guarantee the destructor for the map lock is released // before map needs to be acquired again in self.functions.write() below { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 1373d1bc46e..6b19aff2674 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -396,11 +396,11 @@ impl<'a> FunctionContext<'a> { /// return a reference to each element, for use with the store instruction. fn codegen_array_index( &mut self, - array: super::ir::value::ValueId, - index: super::ir::value::ValueId, + array: ValueId, + index: ValueId, element_type: &ast::Type, location: Location, - length: Option, + length: Option, ) -> Result { // base_index = index * type_size let index = self.make_array_index(index); @@ -438,11 +438,7 @@ impl<'a> FunctionContext<'a> { /// Prepare a slice access. /// Check that the index being used to access a slice element /// is less than the dynamic slice length. - fn codegen_slice_access_check( - &mut self, - index: super::ir::value::ValueId, - length: Option, - ) { + fn codegen_slice_access_check(&mut self, index: ValueId, length: Option) { let index = self.make_array_index(index); // We convert the length as an array index type for comparison let array_len = self diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs index a3ce069b204..1fd1e313c44 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -8,7 +8,7 @@ use crate::ast::{ use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; use crate::node_interner::{ExprId, QuotedTypeId}; -use crate::token::{Attributes, Token, Tokens}; +use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; @@ -478,6 +478,20 @@ pub struct FunctionDefinition { pub return_visibility: Visibility, } +impl FunctionDefinition { + pub fn is_private(&self) -> bool { + self.visibility == ItemVisibility::Private + } + + pub fn is_test(&self) -> bool { + if let Some(attribute) = &self.attributes.function { + matches!(attribute, FunctionAttribute::Test(..)) + } else { + false + } + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Param { pub visibility: Visibility, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs index afa2e7fa7a8..2b78c02e53c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -267,6 +267,14 @@ impl<'context> Elaborator<'context> { let id = self.interner.push_empty_fn(); let module = self.module_id(); self.interner.push_function(id, &function.def, module, location); + + if self.interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + self.interner.register_function(id, &function.def); + } + let functions = vec![(self.local_module, id, function)]; generated_items.functions.push(UnresolvedFunctions { file_id: self.file, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs index da35ca77316..2e4a69c067f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1297,6 +1297,12 @@ impl<'context> Elaborator<'context> { self.current_item = Some(DependencyId::Global(global_id)); let let_stmt = global.stmt_def; + let name = if self.interner.is_in_lsp_mode() { + Some(let_stmt.pattern.name_ident().to_string()) + } else { + None + }; + if !self.in_contract() && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { @@ -1319,8 +1325,9 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); } - self.interner - .add_definition_location(ReferenceId::Global(global_id), Some(self.module_id())); + if let Some(name) = name { + self.interner.register_global(global_id, name, self.module_id()); + } self.local_module = old_module; self.file = old_file; @@ -1458,9 +1465,12 @@ impl<'context> Elaborator<'context> { /// True if we're currently within a constrained function. /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { - self.current_item.map_or(true, |id| match id { - DependencyId::Function(id) => !self.interner.function_modifiers(&id).is_unconstrained, - _ => true, - }) + !self.in_comptime_context() + && self.current_item.map_or(true, |id| match id { + DependencyId::Function(id) => { + !self.interner.function_modifiers(&id).is_unconstrained + } + _ => true, + }) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 4ed72cc0263..cec4b1fd434 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -47,10 +47,13 @@ impl<'local, 'context> Interpreter<'local, 'context> { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), "as_slice" => as_slice(interner, arguments, location), + "expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location), "expr_as_bool" => expr_as_bool(arguments, return_type, location), "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), "expr_as_if" => expr_as_if(arguments, return_type, location), "expr_as_index" => expr_as_index(arguments, return_type, location), + "expr_as_integer" => expr_as_integer(arguments, return_type, location), + "expr_as_member_access" => expr_as_member_access(arguments, return_type, location), "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), "is_unconstrained" => Ok(Value::Bool(true)), @@ -838,6 +841,37 @@ fn expr_as_index( }) } +// fn as_integer(self) -> Option<(Field, bool)> +fn expr_as_integer( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Literal(Literal::Integer(field, sign)) = expr { + Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + } else { + None + } + }) +} + +// fn as_member_access(self) -> Option<(Expr, Quoted)> +fn expr_as_member_access( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type, location, |expr| { + if let ExpressionKind::MemberAccess(member_access) = expr { + let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]); + Some(Value::Tuple(vec![Value::Expr(member_access.lhs.kind), Value::Quoted(tokens)])) + } else { + None + } + }) +} + // fn as_unary_op(self) -> Option<(UnaryOp, Expr)> fn expr_as_unary_op( arguments: Vec<(Value, Location)>, @@ -856,11 +890,11 @@ fn expr_as_unary_op( let unary_op_type = tuple_types.pop().unwrap(); // These values should match the values used in noir_stdlib/src/meta/op.nr - let unary_op_value = match prefix_expr.operator { - UnaryOp::Minus => 0_u128, - UnaryOp::Not => 1_u128, - UnaryOp::MutableReference => 2_u128, - UnaryOp::Dereference { .. } => 3_u128, + let unary_op_value: u128 = match prefix_expr.operator { + UnaryOp::Minus => 0, + UnaryOp::Not => 1, + UnaryOp::MutableReference => 2, + UnaryOp::Dereference { .. } => 3, }; let mut fields = HashMap::default(); @@ -875,6 +909,39 @@ fn expr_as_unary_op( }) } +// fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> +fn expr_as_binary_op( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(arguments, return_type.clone(), location, |expr| { + if let ExpressionKind::Infix(infix_expr) = expr { + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 3); + + tuple_types.pop().unwrap(); + let binary_op_type = tuple_types.pop().unwrap(); + + // For the op value we use the enum member index, which should match noir_stdlib/src/meta/op.nr + let binary_op_value = infix_expr.operator.contents as u128; + + let mut fields = HashMap::default(); + fields.insert(Rc::new("op".to_string()), Value::Field(binary_op_value.into())); + + let unary_op = Value::Struct(fields, binary_op_type); + let lhs = Value::Expr(infix_expr.lhs.kind); + let rhs = Value::Expr(infix_expr.rhs.kind); + Some(Value::Tuple(vec![lhs, unary_op, rhs])) + } else { + None + } + }) +} + // fn as_tuple(self) -> Option<[Expr]> fn expr_as_tuple( arguments: Vec<(Value, Location)>, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index be2afd13507..6436c2562bc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -14,7 +14,7 @@ use crate::ast::{ TypeImpl, }; use crate::macros_api::NodeInterner; -use crate::node_interner::{ModuleAttributes, ReferenceId}; +use crate::node_interner::ModuleAttributes; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, @@ -232,6 +232,13 @@ impl<'a> ModCollector<'a> { let location = Location::new(function.span(), self.file_id); context.def_interner.push_function(func_id, &function.def, module, location); + if context.def_interner.is_in_lsp_mode() + && !function.def.is_test() + && !function.def.is_private() + { + context.def_interner.register_function(func_id, &function.def); + } + // Now link this func_id to a crate level map with the noir function and the module id // Encountering a NoirFunction, we retrieve it's module_data to get the namespace // Once we have lowered it to a HirFunction, we retrieve it's Id from the DefInterner @@ -307,8 +314,8 @@ impl<'a> ModCollector<'a> { }; // Add the struct to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_struct(name, id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_struct(name.clone(), id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -322,10 +329,10 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Struct(id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_struct(id, name.to_string(), parent_module_id); + } } definition_errors } @@ -383,7 +390,7 @@ impl<'a> ModCollector<'a> { // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] - .declare_type_alias(name, type_alias_id); + .declare_type_alias(name.clone(), type_alias_id); if let Err((first_def, second_def)) = result { let err = DefCollectorErrorKind::Duplicate { @@ -396,10 +403,11 @@ impl<'a> ModCollector<'a> { self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); - context.def_interner.add_definition_location( - ReferenceId::Alias(type_alias_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + let name = name.to_string(); + context.def_interner.register_type_alias(type_alias_id, name, parent_module_id); + } } errors } @@ -432,8 +440,8 @@ impl<'a> ModCollector<'a> { }; // Add the trait to scope so its path can be looked up later - let result = - self.def_collector.def_map.modules[self.module_id.0].declare_trait(name, trait_id); + let result = self.def_collector.def_map.modules[self.module_id.0] + .declare_trait(name.clone(), trait_id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -567,10 +575,10 @@ impl<'a> ModCollector<'a> { }; context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); - context.def_interner.add_definition_location( - ReferenceId::Trait(trait_id), - Some(ModuleId { krate, local_id: self.module_id }), - ); + if context.def_interner.is_in_lsp_mode() { + let parent_module_id = ModuleId { krate, local_id: self.module_id }; + context.def_interner.register_trait(trait_id, name.to_string(), parent_module_id); + } self.def_collector.items.traits.insert(trait_id, unresolved); } @@ -766,6 +774,10 @@ impl<'a> ModCollector<'a> { parent: self.module_id, }, ); + + if context.def_interner.is_in_lsp_mode() { + context.def_interner.register_module(mod_id, mod_name.0.contents.clone()); + } } Ok(mod_id) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs index 54d092f9515..a487bda81b3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_def.rs @@ -3,7 +3,7 @@ use crate::node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}; use super::ModuleId; /// A generic ID that references either a module, function, type, interface or global -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ModuleDefId { ModuleId(ModuleId), FunctionId(FuncId), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index c437676b605..0ac13a58ecf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -1,9 +1,10 @@ use fm::FileId; use noirc_errors::Location; use rangemap::RangeMap; -use rustc_hash::FxHashMap; +use rustc_hash::FxHashMap as HashMap; use crate::{ + ast::{FunctionDefinition, ItemVisibility}, hir::def_map::{ModuleDefId, ModuleId}, macros_api::{NodeInterner, StructId}, node_interner::{DefinitionId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, @@ -12,7 +13,7 @@ use petgraph::prelude::NodeIndex as PetGraphIndex; #[derive(Debug, Default)] pub(crate) struct LocationIndices { - map_file_to_range: FxHashMap>, + map_file_to_range: HashMap>, } impl LocationIndices { @@ -275,4 +276,75 @@ impl NodeInterner { .neighbors_directed(reference_index, petgraph::Direction::Outgoing) .next() } + + pub(crate) fn register_module(&mut self, id: ModuleId, name: String) { + self.register_name_for_auto_import(name, ModuleDefId::ModuleId(id), ItemVisibility::Public); + } + + pub(crate) fn register_global( + &mut self, + id: GlobalId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Global(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::GlobalId(id), visibility); + } + + pub(crate) fn register_struct( + &mut self, + id: StructId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Struct(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeId(id), visibility); + } + + pub(crate) fn register_trait(&mut self, id: TraitId, name: String, parent_module_id: ModuleId) { + self.add_definition_location(ReferenceId::Trait(id), Some(parent_module_id)); + + self.register_name_for_auto_import(name, ModuleDefId::TraitId(id), ItemVisibility::Public); + } + + pub(crate) fn register_type_alias( + &mut self, + id: TypeAliasId, + name: String, + parent_module_id: ModuleId, + ) { + self.add_definition_location(ReferenceId::Alias(id), Some(parent_module_id)); + + let visibility = ItemVisibility::Public; + self.register_name_for_auto_import(name, ModuleDefId::TypeAliasId(id), visibility); + } + + pub(crate) fn register_function(&mut self, id: FuncId, func_def: &FunctionDefinition) { + self.register_name_for_auto_import( + func_def.name.0.contents.clone(), + ModuleDefId::FunctionId(id), + func_def.visibility, + ); + } + + fn register_name_for_auto_import( + &mut self, + name: String, + module_def_id: ModuleDefId, + visibility: ItemVisibility, + ) { + if !self.lsp_mode { + return; + } + + self.auto_import_names.entry(name).or_default().push((module_def_id, visibility)); + } + + pub fn get_auto_import_names(&self) -> &HashMap> { + &self.auto_import_names + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index e9d1dbb67f8..46ec2676930 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -19,6 +19,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::macros_api::ModuleDefId; use crate::macros_api::UnaryOp; use crate::QuotedType; @@ -231,6 +232,11 @@ pub struct NodeInterner { // (ReferenceId::Reference and ReferenceId::Local aren't included here) pub(crate) reference_modules: HashMap, + // All names (and their definitions) that can be offered for auto_import. + // These include top-level functions, global variables and types, but excludes + // impl and trait-impl methods. + pub(crate) auto_import_names: HashMap>, + /// Each value currently in scope in the comptime interpreter. /// Each element of the Vec represents a scope with every scope together making /// up all currently visible definitions. The first scope is always the global scope. @@ -602,6 +608,7 @@ impl Default for NodeInterner { reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::default(), reference_modules: HashMap::default(), + auto_import_names: HashMap::default(), comptime_scopes: vec![HashMap::default()], } } @@ -910,6 +917,7 @@ impl NodeInterner { // This needs to be done after pushing the definition since it will reference the // location that was stored self.add_definition_location(ReferenceId::Function(id), Some(module)); + definition_id } diff --git a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml index 5140f5a5a8c..8bb56703e8a 100644 --- a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/compiler/wasm/Cargo.toml b/noir/noir-repo/compiler/wasm/Cargo.toml index 20272dfeecb..c8b8c3bb06e 100644 --- a/noir/noir-repo/compiler/wasm/Cargo.toml +++ b/noir/noir-repo/compiler/wasm/Cargo.toml @@ -6,8 +6,10 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lints] +workspace = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index e939cd583e8..18417b376e5 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -216,7 +216,8 @@ "wasi", "wasmer", "Weierstraß", - "zshell" + "zshell", + "Linea" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md index c8a42c379e6..3ef6e014a2f 100644 --- a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md +++ b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md @@ -86,7 +86,7 @@ A Noir program makes a statement that can be verified. It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). A Noir program compiles to an Abstract Circuit Intermediate Representation which is: - - A tree structure + - Conceptually a tree structure - Leaves (inputs) are the `Field` type - Nodes contain arithmetic operations to combine them (gates) - The root is the final result (return value) @@ -99,12 +99,16 @@ You can dig deeper and use the `--print-acir` param to take a closer look at ind ### Use the `Field` type Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. +Some things to be mindful of when using a Field type for a regular integer value: +- A variable of type `Field` can be cast `as` an integer type (eg `u8`, `u64`) + - Note: this retains only the bits of the integer type. Eg a Field value of 260 as a `u8` becomes 4 +- For Field types arithmetic operations meaningfully overflow/underflow, yet for integer types they are checked according to their size +- Comparisons and bitwise operations do not exist for `Field`s, cast to an appropriately sized integer type when you need to :::tip Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates ::: -**Note:** Need to remain mindful of overflow. Types with less bits may be used to limit the range of possible values prior to a calculation. ### Use Arithmetic over non-arithmetic operations diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md index c800d91ac69..3bb96c66795 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/how_to/how-to-solidity-verifier.md @@ -133,7 +133,7 @@ function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) externa When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. Note that the public inputs must be passed in separately to the rest of the proof so we must split the proof as returned from `bb`. -First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the +First generate a proof with `bb` at the location `./proof` using the steps in [get started](../getting_started/hello_noir/index.md), this proof is in a binary format but we want to convert it into a hex string to pass into Remix, this can be done with the ```bash # This value must be changed to match the number of public inputs (including return values!) in your program. @@ -239,6 +239,12 @@ For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently suppo - Polygon PoS - Scroll - Celo +- BSC +- Blast L2 +- Avalanche C-Chain +- Mode +- Linea +- Moonbeam If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. diff --git a/noir/noir-repo/noir_stdlib/src/meta/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index 3230ce42457..55c49531017 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -1,7 +1,14 @@ use crate::option::Option; use crate::meta::op::UnaryOp; +use crate::meta::op::BinaryOp; impl Expr { + #[builtin(expr_as_integer)] + fn as_integer(self) -> Option<(Field, bool)> {} + + #[builtin(expr_as_binary_op)] + fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> {} + #[builtin(expr_as_bool)] fn as_bool(self) -> Option {} @@ -14,6 +21,9 @@ impl Expr { #[builtin(expr_as_index)] fn as_index(self) -> Option<(Expr, Expr)> {} + #[builtin(expr_as_member_access)] + fn as_member_access(self) -> Option<(Expr, Quoted)> {} + #[builtin(expr_as_tuple)] fn as_tuple(self) -> Option<[Expr]> {} diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index beaee8e44ea..d16a8648bc2 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -45,7 +45,7 @@ pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted result } -unconstrained pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { +pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) { HANDLERS.insert(t, f); } diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr index 1fbd4884841..ebd89677c50 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/op.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -19,3 +19,74 @@ impl UnaryOp { self.op == 3 } } + +struct BinaryOp { + op: Field +} + +impl BinaryOp { + fn is_add(self) -> bool { + self.op == 0 + } + + fn is_subtract(self) -> bool { + self.op == 1 + } + + fn is_multiply(self) -> bool { + self.op == 2 + } + + fn is_divide(self) -> bool { + self.op == 3 + } + + fn is_equal(self) -> bool { + self.op == 4 + } + + fn is_not_equal(self) -> bool { + self.op == 5 + } + + fn is_less(self) -> bool { + self.op == 6 + } + + fn is_less_equal(self) -> bool { + self.op == 7 + } + + fn is_greater(self) -> bool { + self.op == 8 + } + + fn is_greater_or_equal(self) -> bool { + self.op == 9 + } + + fn is_and(self) -> bool { + self.op == 10 + } + + fn is_or(self) -> bool { + self.op == 11 + } + + fn is_xor(self) -> bool { + self.op == 12 + } + + fn is_shift_right(self) -> bool { + self.op == 13 + } + + fn is_shift_left(self) -> bool { + self.op == 14 + } + + fn is_modulo(self) -> bool { + self.op == 15 + } +} + diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr index eb0d27e2a38..7b25dcb5097 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_exp/src/main.nr @@ -1,3 +1,6 @@ +use std::meta::op::UnaryOp; +use std::meta::op::BinaryOp; + fn main() { comptime { @@ -36,20 +39,49 @@ fn main() { assert_eq(expr.as_bool().unwrap(), true); // Check Expr::as_unary_op - let expr = quote { -x }.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - assert(op.is_minus()); + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); + + // Check Expr::as_binary_op + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x > y }).is_greater()); + assert(get_binary_op(quote { x >= y }).is_greater_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); - let expr = quote { !x }.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - assert(op.is_not()); + // Check Expr::as_integer + let expr = quote { 1 }.as_expr().unwrap(); + assert_eq((1, false), expr.as_integer().unwrap()); - let expr = quote { &mut x }.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - assert(op.is_mutable_reference()); + let expr = quote { -2 }.as_expr().unwrap(); + assert_eq((2, true), expr.as_integer().unwrap()); - let expr = quote { *x }.as_expr().unwrap(); - let (op, _) = expr.as_unary_op().unwrap(); - assert(op.is_dereference()); + // Check Expr::as_member_access + let expr = quote { foo.bar }.as_expr().unwrap(); + let (_, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); } } + +comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op +} + +comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/macros_in_comptime/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/macros_in_comptime/src/main.nr index 31d08d2762d..c5cc7880112 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/macros_in_comptime/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/macros_in_comptime/src/main.nr @@ -13,7 +13,7 @@ fn main() { // Call a different function from the interpreter, then have the // elaborator switch to the middle of foo from its previous scope in main -unconstrained comptime fn foo(x: Field) { +comptime fn foo(x: Field) { assert(modulus_num_bits() != 0); let cond = quote { modulus_num_bits() != 0 }; diff --git a/noir/noir-repo/tooling/acvm_cli/Cargo.toml b/noir/noir-repo/tooling/acvm_cli/Cargo.toml index a592f2d65f3..06dd5e676bd 100644 --- a/noir/noir-repo/tooling/acvm_cli/Cargo.toml +++ b/noir/noir-repo/tooling/acvm_cli/Cargo.toml @@ -10,6 +10,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `acvm_cli` to `acvm` diff --git a/noir/noir-repo/tooling/debugger/Cargo.toml b/noir/noir-repo/tooling/debugger/Cargo.toml index 540d6d11bc0..b9b83d86836 100644 --- a/noir/noir-repo/tooling/debugger/Cargo.toml +++ b/noir/noir-repo/tooling/debugger/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [build-dependencies] build-data.workspace = true diff --git a/noir/noir-repo/tooling/fuzzer/Cargo.toml b/noir/noir-repo/tooling/fuzzer/Cargo.toml index 106d8abead1..31701991844 100644 --- a/noir/noir-repo/tooling/fuzzer/Cargo.toml +++ b/noir/noir-repo/tooling/fuzzer/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index 03c6c9105ba..c5203478f5a 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true# rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index 796137a8e9f..366b158ad89 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -30,7 +30,7 @@ use lsp_types::{ use nargo::{ package::{Package, PackageType}, parse_all, - workspace::{self, Workspace}, + workspace::Workspace, }; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; @@ -384,7 +384,7 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { pub fn insert_all_files_for_workspace_into_file_manager( state: &LspState, - workspace: &workspace::Workspace, + workspace: &Workspace, file_manager: &mut FileManager, ) { // First add files we cached: these have the source code of files that are modified @@ -411,7 +411,7 @@ fn prepare_package_from_source_string() { let client = ClientSocket::new_closed(); let mut state = LspState::new(&client, acvm::blackbox_solver::StubbedBlackBoxSolver); - let (mut context, crate_id) = crate::prepare_source(source.to_string(), &mut state); + let (mut context, crate_id) = prepare_source(source.to_string(), &mut state); let _check_result = noirc_driver::check_crate(&mut context, crate_id, &Default::default()); let main_func_id = context.get_main_function(&crate_id); assert!(main_func_id.is_some()); diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 3a60de15c4a..4d2186badc3 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -223,7 +223,7 @@ mod notification_tests { use super::*; use lsp_types::{ - InlayHintLabel, InlayHintParams, Position, TextDocumentContentChangeEvent, + InlayHintLabel, InlayHintParams, Position, Range, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier, WorkDoneProgressParams, }; @@ -270,9 +270,9 @@ mod notification_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: 0, character: 0 }, - end: lsp_types::Position { line: 1, character: 0 }, + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 1, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index dd3131aba56..ad0cf0c5b9b 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -8,7 +8,7 @@ use completion_items::{ crate_completion_item, field_completion_item, simple_completion_item, struct_field_completion_item, }; -use fm::{FileId, PathString}; +use fm::{FileId, FileMap, PathString}; use kinds::{FunctionCompletionKind, FunctionKind, ModuleCompletionKind, RequestedItems}; use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse}; use noirc_errors::{Location, Span}; @@ -33,10 +33,11 @@ use noirc_frontend::{ }; use sort_text::underscore_sort_text; -use crate::{utils, LspState}; +use crate::{requests::to_lsp_location, utils, LspState}; use super::process_request; +mod auto_import; mod builtins; mod completion_items; mod kinds; @@ -65,7 +66,9 @@ pub(crate) fn on_completion_request( let (parsed_module, _errors) = noirc_frontend::parse_program(source); let mut finder = NodeFinder::new( + args.files, file_id, + source, byte_index, byte, args.crate_id, @@ -81,7 +84,9 @@ pub(crate) fn on_completion_request( } struct NodeFinder<'a> { + files: &'a FileMap, file: FileId, + lines: Vec<&'a str>, byte_index: usize, byte: Option, /// The module ID of the current file. @@ -100,11 +105,20 @@ struct NodeFinder<'a> { /// Type parameters in the current scope. These are collected when entering /// a struct, a function, etc., and cleared afterwards. type_parameters: HashSet, + /// ModuleDefIds we already suggested, so we don't offer these for auto-import. + suggested_module_def_ids: HashSet, + /// How many nested `mod` we are in deep + nesting: usize, + /// The line where an auto_import must be inserted + auto_import_line: usize, } impl<'a> NodeFinder<'a> { + #[allow(clippy::too_many_arguments)] fn new( + files: &'a FileMap, file: FileId, + source: &'a str, byte_index: usize, byte: Option, krate: CrateId, @@ -124,7 +138,9 @@ impl<'a> NodeFinder<'a> { }; let module_id = ModuleId { krate, local_id }; Self { + files, file, + lines: source.lines().collect(), byte_index, byte, root_module_id, @@ -135,6 +151,9 @@ impl<'a> NodeFinder<'a> { completion_items: Vec::new(), local_variables: HashMap::new(), type_parameters: HashSet::new(), + suggested_module_def_ids: HashSet::new(), + nesting: 0, + auto_import_line: 0, } } @@ -158,6 +177,12 @@ impl<'a> NodeFinder<'a> { } fn find_in_item(&mut self, item: &Item) { + if let ItemKind::Import(..) = &item.kind { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.end.line + 1) as usize; + } + } + if !self.includes_span(item.span) { return; } @@ -180,10 +205,19 @@ impl<'a> NodeFinder<'a> { ModuleId { krate: self.module_id.krate, local_id: *child_module }; } + let old_auto_import_line = self.auto_import_line; + self.nesting += 1; + + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.start.line + 1) as usize; + } + self.find_in_parsed_module(&parsed_sub_module.contents); // Restore the old module before continuing self.module_id = previous_module_id; + self.nesting -= 1; + self.auto_import_line = old_auto_import_line; } ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), @@ -714,6 +748,7 @@ impl<'a> NodeFinder<'a> { self.type_parameters_completion(&prefix); } } + self.complete_auto_imports(&prefix, requested_items); } } @@ -935,6 +970,7 @@ impl<'a> NodeFinder<'a> { function_kind, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); } } } @@ -955,6 +991,7 @@ impl<'a> NodeFinder<'a> { function_kind, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); } } } @@ -1038,6 +1075,7 @@ impl<'a> NodeFinder<'a> { requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } @@ -1050,6 +1088,7 @@ impl<'a> NodeFinder<'a> { requested_items, ) { self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); } } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs new file mode 100644 index 00000000000..c5710d30b5c --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -0,0 +1,195 @@ +use lsp_types::{Position, Range, TextEdit}; +use noirc_frontend::{ + ast::ItemVisibility, + graph::{CrateId, Dependency}, + hir::def_map::ModuleId, + macros_api::{ModuleDefId, NodeInterner}, + node_interner::ReferenceId, +}; + +use super::{ + kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}, + name_matches, + sort_text::auto_import_sort_text, + NodeFinder, +}; + +impl<'a> NodeFinder<'a> { + pub(super) fn complete_auto_imports(&mut self, prefix: &str, requested_items: RequestedItems) { + for (name, entries) in self.interner.get_auto_import_names() { + if !name_matches(name, prefix) { + continue; + } + + for (module_def_id, visibility) in entries { + if self.suggested_module_def_ids.contains(module_def_id) { + continue; + } + + let Some(mut completion_item) = self.module_def_id_completion_item( + *module_def_id, + name.clone(), + FunctionCompletionKind::NameAndParameters, + FunctionKind::Any, + requested_items, + ) else { + continue; + }; + + let module_full_path; + if let ModuleDefId::ModuleId(module_id) = module_def_id { + module_full_path = module_id_path( + module_id, + &self.module_id, + self.interner, + self.dependencies, + ); + } else { + let Some(parent_module) = get_parent_module(self.interner, *module_def_id) + else { + continue; + }; + + match *visibility { + ItemVisibility::Public => (), + ItemVisibility::Private => { + // Technically this can't be reached because we don't record private items for auto-import, + // but this is here for completeness. + continue; + } + ItemVisibility::PublicCrate => { + if self.module_id.krate != parent_module.krate { + continue; + } + } + } + + module_full_path = module_id_path( + parent_module, + &self.module_id, + self.interner, + self.dependencies, + ); + } + + let full_path = if let ModuleDefId::ModuleId(..) = module_def_id { + module_full_path + } else { + format!("{}::{}", module_full_path, name) + }; + + let mut label_details = completion_item.label_details.unwrap(); + label_details.detail = Some(format!("(use {})", full_path)); + completion_item.label_details = Some(label_details); + + let line = self.auto_import_line as u32; + let character = (self.nesting * 4) as u32; + let indent = " ".repeat(self.nesting * 4); + let mut newlines = "\n"; + + // If the line we are inserting into is not an empty line, insert an extra line to make some room + if let Some(line_text) = self.lines.get(line as usize) { + if !line_text.trim().is_empty() { + newlines = "\n\n"; + } + } + + completion_item.additional_text_edits = Some(vec![TextEdit { + range: Range { + start: Position { line, character }, + end: Position { line, character }, + }, + new_text: format!("use {};{}{}", full_path, newlines, indent), + }]); + + completion_item.sort_text = Some(auto_import_sort_text()); + + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(*module_def_id); + } + } + } +} + +fn get_parent_module(interner: &NodeInterner, module_def_id: ModuleDefId) -> Option<&ModuleId> { + let reference_id = module_def_id_to_reference_id(module_def_id); + interner.reference_module(reference_id) +} + +fn module_def_id_to_reference_id(module_def_id: ModuleDefId) -> ReferenceId { + match module_def_id { + ModuleDefId::ModuleId(id) => ReferenceId::Module(id), + ModuleDefId::FunctionId(id) => ReferenceId::Function(id), + ModuleDefId::TypeId(id) => ReferenceId::Struct(id), + ModuleDefId::TypeAliasId(id) => ReferenceId::Alias(id), + ModuleDefId::TraitId(id) => ReferenceId::Trait(id), + ModuleDefId::GlobalId(id) => ReferenceId::Global(id), + } +} + +/// Computes the path of `module_id` relative to `current_module_id`. +/// If it's not relative, the full path is returned. +fn module_id_path( + module_id: &ModuleId, + current_module_id: &ModuleId, + interner: &NodeInterner, + dependencies: &[Dependency], +) -> String { + let mut string = String::new(); + + let crate_id = module_id.krate; + let crate_name = match crate_id { + CrateId::Root(_) => Some("crate".to_string()), + CrateId::Crate(_) => dependencies + .iter() + .find(|dep| dep.crate_id == crate_id) + .map(|dep| format!("{}", dep.name)), + CrateId::Stdlib(_) => Some("std".to_string()), + CrateId::Dummy => None, + }; + + let wrote_crate = if let Some(crate_name) = crate_name { + string.push_str(&crate_name); + true + } else { + false + }; + + let Some(module_attributes) = interner.try_module_attributes(module_id) else { + return string; + }; + + if wrote_crate { + string.push_str("::"); + } + + let mut segments = Vec::new(); + let mut current_attributes = module_attributes; + loop { + let parent_module_id = + &ModuleId { krate: module_id.krate, local_id: current_attributes.parent }; + + // If the parent module is the current module we stop because we want a relative path to the module + if current_module_id == parent_module_id { + // When the path is relative we don't want the "crate::" prefix anymore + string = string.strip_prefix("crate::").unwrap_or(&string).to_string(); + break; + } + + let Some(parent_attributes) = interner.try_module_attributes(parent_module_id) else { + break; + }; + + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + + for segment in segments.iter().rev() { + string.push_str(segment); + string.push_str("::"); + } + + string.push_str(&module_attributes.name); + + string +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs index 4a8aebd7988..a1dd0ba5e94 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/sort_text.rs @@ -9,6 +9,11 @@ pub(super) fn default_sort_text() -> String { "5".to_string() } +/// Sort text for auto-import items. We want these to show up after local definitions. +pub(super) fn auto_import_sort_text() -> String { + "6".to_string() +} + /// When completing something like `Foo::`, we want to show methods that take /// self after the other ones. pub(super) fn self_mismatch_sort_text() -> String { diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs index ff028954f7b..825a7dd0081 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -10,7 +10,7 @@ mod completion_tests { field_completion_item, module_completion_item, simple_completion_item, snippet_completion_item, }, - sort_text::self_mismatch_sort_text, + sort_text::{auto_import_sort_text, self_mismatch_sort_text}, }, on_completion_request, }, @@ -18,9 +18,10 @@ mod completion_tests { }; use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse, - DidOpenTextDocumentParams, PartialResultParams, Position, TextDocumentIdentifier, - TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionParams, + CompletionResponse, DidOpenTextDocumentParams, PartialResultParams, Position, Range, + TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, TextEdit, + WorkDoneProgressParams, }; use tokio::test; @@ -63,14 +64,13 @@ mod completion_tests { }, ) .await - .expect("Could not execute on_completion_request") - .unwrap(); + .expect("Could not execute on_completion_request"); - let CompletionResponse::Array(items) = response else { - panic!("Expected response to be CompletionResponse::Array"); - }; - - items + if let Some(CompletionResponse::Array(items)) = response { + items + } else { + vec![] + } } fn assert_items_match(mut items: Vec, mut expected: Vec) { @@ -97,6 +97,12 @@ mod completion_tests { assert_items_match(items, expected); } + async fn assert_completion_excluding_auto_import(src: &str, expected: Vec) { + let items = get_completions(src).await; + let items = items.into_iter().filter(|item| item.additional_text_edits.is_none()).collect(); + assert_items_match(items, expected); + } + pub(super) fn function_completion_item( label: impl Into, insert_text: impl Into, @@ -368,7 +374,7 @@ mod completion_tests { l>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "local", @@ -388,7 +394,7 @@ mod completion_tests { l>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "local", @@ -406,7 +412,7 @@ mod completion_tests { l>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "local", @@ -426,7 +432,11 @@ mod completion_tests { h>|< } "#; - assert_completion(src, vec![function_completion_item("hello()", "hello()", "fn()")]).await; + assert_completion_excluding_auto_import( + src, + vec![function_completion_item("hello()", "hello()", "fn()")], + ) + .await; } #[test] @@ -438,7 +448,7 @@ mod completion_tests { h>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![function_completion_item( "hello(…)", @@ -456,7 +466,7 @@ mod completion_tests { a>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![ snippet_completion_item( @@ -488,7 +498,7 @@ mod completion_tests { } } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "SomeStruct", @@ -511,7 +521,7 @@ mod completion_tests { } } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "SomeStruct", @@ -531,7 +541,7 @@ mod completion_tests { } } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "index", @@ -551,7 +561,7 @@ mod completion_tests { lambda(|var| v>|<) } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "var", @@ -742,7 +752,7 @@ mod completion_tests { let x = t>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "true", @@ -766,7 +776,7 @@ mod completion_tests { } } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![ simple_completion_item( @@ -794,7 +804,7 @@ mod completion_tests { } } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![ simple_completion_item( @@ -822,7 +832,7 @@ mod completion_tests { g>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "good", @@ -844,7 +854,7 @@ mod completion_tests { } } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![ simple_completion_item( @@ -870,7 +880,7 @@ mod completion_tests { g>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item( "good", @@ -888,7 +898,7 @@ mod completion_tests { context: C>|< } "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item("Context", CompletionItemKind::TYPE_PARAMETER, None)], ) @@ -954,7 +964,7 @@ mod completion_tests { let src = r#" fn foo(x: C>|<) {} "#; - assert_completion( + assert_completion_excluding_auto_import( src, vec![simple_completion_item("Context", CompletionItemKind::TYPE_PARAMETER, None)], ) @@ -1323,4 +1333,227 @@ mod completion_tests { assert_completion(src, vec![function_completion_item("one()", "one()", "fn() -> Self")]) .await; } + + #[test] + async fn test_auto_imports() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + + struct Foo { + } + + impl Foo { + // This is here to make sure it's not offered for completion + fn hello_world() {} + } + } + } + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 0, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + + assert_eq!(item.sort_text, Some(auto_import_sort_text())); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + fn foo() { + hel>|< + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 2, character: 4 }, + end: Position { line: 2, character: 4 }, + }, + new_text: "use bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_imports_when_in_nested_module_and_item_is_not_further_nested() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + + mod baz { + fn foo() { + hel>|< + } + } + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "hello_world()"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use crate::foo::bar::hello_world)".to_string()), + description: Some("fn()".to_string()) + }) + ); + + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 7, character: 8 }, + end: Position { line: 7, character: 8 }, + }, + new_text: "use crate::foo::bar::hello_world;\n\n ".to_string(), + }]) + ); + } + + #[test] + async fn test_auto_import_inserts_after_last_use() { + let src = r#" + mod foo { + mod bar { + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!( + item.additional_text_edits, + Some(vec![TextEdit { + range: Range { + start: Position { line: 8, character: 0 }, + end: Position { line: 8, character: 0 }, + }, + new_text: "use foo::bar::hello_world;\n".to_string(), + }]) + ); + } + + #[test] + async fn test_does_not_auto_import_test_functions() { + let src = r#" + mod foo { + mod bar { + #[test] + pub fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_does_not_auto_import_private_functions() { + let src = r#" + mod foo { + mod bar { + fn hello_world() {} + } + } + + use foo::bar; + + fn main() { + hel>|< + } + "#; + let items = get_completions(src).await; + assert!(items.is_empty()); + } + + #[test] + async fn test_auto_import_suggests_modules_too() { + let src = r#" + mod foo { + mod barbaz { + fn hello_world() {} + } + } + + fn main() { + barb>|< + } + "#; + let items = get_completions(src).await; + assert_eq!(items.len(), 1); + + let item = &items[0]; + assert_eq!(item.label, "barbaz"); + assert_eq!( + item.label_details, + Some(CompletionItemLabelDetails { + detail: Some("(use foo::barbaz)".to_string()), + description: None + }) + ); + } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs index 8dd63963b5d..a1e083187d3 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs @@ -722,9 +722,9 @@ mod inlay_hints_tests { InlayHintParams { work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, text_document: TextDocumentIdentifier { uri: noir_text_document }, - range: lsp_types::Range { - start: lsp_types::Position { line: start_line, character: 0 }, - end: lsp_types::Position { line: end_line, character: 0 }, + range: Range { + start: Position { line: start_line, character: 0 }, + end: Position { line: end_line, character: 0 }, }, }, ) diff --git a/noir/noir-repo/tooling/nargo/Cargo.toml b/noir/noir-repo/tooling/nargo/Cargo.toml index 56e88dacf2d..046eca88099 100644 --- a/noir/noir-repo/tooling/nargo/Cargo.toml +++ b/noir/noir-repo/tooling/nargo/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index dabb779ae8d..f3d9f92caaa 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -8,6 +8,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # Rename binary from `nargo_cli` to `nargo` diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 05b2fdb7d52..9868f259097 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + [dependencies] bytecount = "0.6.3" noirc_frontend.workspace = true diff --git a/noir/noir-repo/tooling/nargo_toml/Cargo.toml b/noir/noir-repo/tooling/nargo_toml/Cargo.toml index 7c9faa4562a..e4766e44859 100644 --- a/noir/noir-repo/tooling/nargo_toml/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_toml/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_abi/Cargo.toml b/noir/noir-repo/tooling/noirc_abi/Cargo.toml index 4c0c1f75e42..a7baf334bff 100644 --- a/noir/noir-repo/tooling/noirc_abi/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs index aecb620b79d..b79f232d9e8 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/arbitrary.rs @@ -107,10 +107,8 @@ pub(super) fn arb_abi_type() -> BoxedStrategy { (1..10u32, inner.clone()) .prop_map(|(length, typ)| { AbiType::Array { length, typ: Box::new(typ) } }) .boxed(), - prop::collection::vec(inner.clone(), 1..10) - .prop_map(|fields| { AbiType::Tuple { fields } }) - .boxed(), - (".*", prop::collection::vec((".+", inner), 1..10)) + vec(inner.clone(), 1..10).prop_map(|fields| { AbiType::Tuple { fields } }).boxed(), + (".*", vec((".+", inner), 1..10)) .prop_map(|(path, mut fields)| { // Require that all field names are unique. ensure_unique_strings(fields.iter_mut().map(|(field_name, _)| field_name)); @@ -141,7 +139,7 @@ fn arb_abi_param(typ: AbiType) -> SBoxedStrategy { prop_compose! { pub(super) fn arb_abi_and_input_map() - (mut parameters_with_values in proptest::collection::vec(arb_abi_param_and_value(), 0..100), return_type: Option) + (mut parameters_with_values in vec(arb_abi_param_and_value(), 0..100), return_type: Option) -> (Abi, InputMap) { // Require that all parameter names are unique. ensure_unique_strings(parameters_with_values.iter_mut().map(|(param_name,_)| &mut param_name.name)); diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs index ef4a468b661..b0c11979d17 100644 --- a/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi_wasm/src/lib.rs @@ -121,8 +121,7 @@ pub fn abi_decode(abi: JsAbi, witness_map: JsWitnessMap) -> Result::from_serde(&return_struct) - .map_err(|err| err.to_string().into()) + ::from_serde(&return_struct).map_err(|err| err.to_string().into()) } #[wasm_bindgen(js_name = serializeWitness)] @@ -155,7 +154,7 @@ pub fn abi_decode_error( AbiErrorType::Custom(typ) => { let input_value = decode_value(&mut raw_error.data.into_iter(), &typ)?; let json_types = JsonTypes::try_from_input_value(&input_value, &typ)?; - ::from_serde(&json_types) + ::from_serde(&json_types) .map_err(|err| err.to_string().into()) } } diff --git a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml index 4249604f949..13ff68e423a 100644 --- a/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_artifacts/Cargo.toml @@ -7,6 +7,9 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml index 0ccd56b791f..136775d5831 100644 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ b/noir/noir-repo/tooling/profiler/Cargo.toml @@ -8,6 +8,9 @@ license.workspace = true rust-version.workspace = true repository.workspace = true +[lints] +workspace = true + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index 7c6e77917a5..0fa12239d05 100644 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -128,7 +128,7 @@ mod tests { } impl GatesProvider for TestGateProvider { - fn get_gates(&self, artifact_path: &std::path::Path) -> eyre::Result { + fn get_gates(&self, artifact_path: &Path) -> eyre::Result { let response = self .mock_responses .get(artifact_path) diff --git a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs index 772c87ad1cb..edb3e1b0f08 100644 --- a/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs +++ b/noir/noir-repo/tooling/profiler/src/opcode_formatter.rs @@ -100,7 +100,7 @@ fn format_binary_field_op(op: &BinaryFieldOp) -> String { } } -fn format_binary_int(op: &acir::brillig::BinaryIntOp) -> String { +fn format_binary_int(op: &BinaryIntOp) -> String { match op { BinaryIntOp::Add => "add".to_string(), BinaryIntOp::Sub => "sub".to_string(),