diff --git a/acvm-repo/acir/src/circuit/brillig.rs b/acvm-repo/acir/src/circuit/brillig.rs index 972a08dd32a..504955c9e13 100644 --- a/acvm-repo/acir/src/circuit/brillig.rs +++ b/acvm-repo/acir/src/circuit/brillig.rs @@ -24,7 +24,7 @@ pub enum BrilligOutputs { /// This is purely a wrapper struct around a list of Brillig opcode's which represents /// a full Brillig function to be executed by the Brillig VM. -/// This is stored separately on a program and accessed through a [BrilligPointer]. +/// This is stored separately on a program and accessed through a [BrilligFunctionId]. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default, Debug, Hash)] #[cfg_attr(feature = "arb", derive(proptest_derive::Arbitrary))] pub struct BrilligBytecode { diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 1a14971efdf..c6aa1d0105f 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -114,7 +114,7 @@ pub enum BlackBoxFuncCall { /// - input: (witness, bit_size) RANGE { input: FunctionInput }, /// Computes the Blake2s hash of the inputs, as specified in - /// https://tools.ietf.org/html/rfc7693 + /// /// - inputs are a byte array, i.e a vector of (witness, 8) /// - output is a byte array of length 32, i.e. an array of 32 /// (witness, 8), constrained to be the blake2s of the inputs. diff --git a/acvm-repo/acvm/src/compiler/mod.rs b/acvm-repo/acvm/src/compiler/mod.rs index ee503e7e0d0..301f6088826 100644 --- a/acvm-repo/acvm/src/compiler/mod.rs +++ b/acvm-repo/acvm/src/compiler/mod.rs @@ -71,7 +71,7 @@ fn transform_assert_messages( .collect() } -/// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] specific optimizations to a [`Circuit`]. +/// Applies backend specific optimizations to a [`Circuit`]. /// /// Runs multiple passes until the output stabilizes. pub fn compile( diff --git a/acvm-repo/acvm/src/compiler/optimizers/mod.rs b/acvm-repo/acvm/src/compiler/optimizers/mod.rs index 3e085e4ba9b..a184ee0f75d 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/mod.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/mod.rs @@ -19,7 +19,7 @@ use self::unused_memory::UnusedMemoryOptimizer; use super::{AcirTransformationMap, transform_assert_messages}; -/// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] independent optimizations to a [`Circuit`]. +/// Applies backend independent optimizations to a [`Circuit`]. pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformationMap) { // Track original acir opcode positions throughout the transformation passes of the compilation // by applying the modifications done to the circuit opcodes and also to the opcode_positions (delete and insert) @@ -34,7 +34,7 @@ pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformati (acir, transformation_map) } -/// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] independent optimizations to a [`Circuit`]. +/// Applies backend independent optimizations to a [`Circuit`]. /// /// Accepts an injected `acir_opcode_positions` to allow optimizations to be applied in a loop. #[tracing::instrument(level = "trace", name = "optimize_acir" skip(acir, acir_opcode_positions))] diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs index fe9404b075c..72d070d80a8 100644 --- a/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -25,7 +25,7 @@ use super::{ /// The value was determined by running tests. const MAX_TRANSFORMER_PASSES: usize = 3; -/// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] specific optimizations to a [`Circuit`]. +/// Applies backend specific optimizations to a [`Circuit`]. pub fn transform( acir: Circuit, expression_width: ExpressionWidth, @@ -44,7 +44,7 @@ pub fn transform( (acir, transformation_map) } -/// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] specific optimizations to a [`Circuit`]. +/// Applies backend specific optimizations to a [`Circuit`]. /// /// Accepts an injected `acir_opcode_positions` to allow transformations to be applied directly after optimizations. /// @@ -87,7 +87,7 @@ pub(super) fn transform_internal( (acir, acir_opcode_positions) } -/// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] specific optimizations to a [`Circuit`]. +/// Applies backend specific optimizations to a [`Circuit`]. /// /// Accepts an injected `acir_opcode_positions` to allow transformations to be applied directly after optimizations. /// diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index 136f358b9bb..e4e42c630ee 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -313,7 +313,7 @@ fn extract_failure_payload_from_memory( } } -/// Encapsulates a request from a Brillig VM process that encounters a [foreign call opcode][acir::brillig_vm::Opcode::ForeignCall] +/// Encapsulates a request from a Brillig VM process that encounters a [foreign call opcode][brillig_vm::brillig::Opcode::ForeignCall] /// where the result of the foreign call has not yet been provided. /// /// The caller must resolve this opcode externally based upon the information in the request. diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 3c66b08eb4c..1f51b9a4de0 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -47,7 +47,7 @@ pub enum ACVMStatus { /// Most commonly this will be due to an unsatisfied constraint due to invalid inputs to the circuit. Failure(OpcodeResolutionError), - /// The ACVM has encountered a request for a Brillig [foreign call][acir::brillig_vm::Opcode::ForeignCall] + /// The ACVM has encountered a request for a Brillig [foreign call][brillig_vm::brillig::Opcode::ForeignCall] /// to retrieve information from outside of the ACVM. The result of the foreign call must be passed back /// to the ACVM using [`ACVM::resolve_pending_foreign_call`]. /// @@ -312,7 +312,7 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver> ACVM<'a, F, B> { } } - /// Resolves a foreign call's [result][acir::brillig_vm::ForeignCallResult] using a result calculated outside of the ACVM. + /// Resolves a foreign call's [result][brillig_vm::brillig::ForeignCallResult] using a result calculated outside of the ACVM. /// /// The ACVM can then be restarted to solve the remaining Brillig VM process as well as the remaining ACIR opcodes. pub fn resolve_pending_foreign_call(&mut self, foreign_call_result: ForeignCallResult) { diff --git a/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs b/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs index 22b88de8ecd..c897043a9b0 100644 --- a/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs +++ b/acvm-repo/bn254_blackbox_solver/src/generator/generators.rs @@ -21,7 +21,7 @@ fn default_generators() -> &'static [Affine; NUM_DEFAULT_GENERATORS] { }) } -/// Derives generator points via [hash-to-curve][hash_to_curve]. +/// Derives generator points. /// /// # ALGORITHM DESCRIPTION /// @@ -29,12 +29,10 @@ fn default_generators() -> &'static [Affine; NUM_DEFAULT_GENERATORS] { /// 2. a 64-byte preimage buffer is generated with the following structure: /// - bytes 0-31: BLAKE3 hash of domain_separator /// - bytes 32-63: generator index in big-endian form -/// 3. The [hash-to-curve algorithm][hash_to_curve] is used to hash the above into a curve point. +/// 3. The hash-to-curve algorithm is used to hash the above into a curve point. /// /// NOTE: The domain separator is included to ensure that it is possible to derive independent sets of /// index-addressable generators. -/// -/// [hash_to_curve]: super::hash_to_curve::hash_to_curve pub fn derive_generators( domain_separator_bytes: &[u8], num_generators: u32, diff --git a/acvm-repo/brillig/src/opcodes.rs b/acvm-repo/brillig/src/opcodes.rs index bd083b914d4..b2527847a15 100644 --- a/acvm-repo/brillig/src/opcodes.rs +++ b/acvm-repo/brillig/src/opcodes.rs @@ -257,7 +257,7 @@ pub enum BrilligOpcode { offset_address: MemoryAddress, }, /// We don't support dynamic jumps or calls - /// See https://github.com/ethereum/aleth/issues/3404 for reasoning + /// See for reasoning Call { location: Label, }, diff --git a/compiler/fm/src/lib.rs b/compiler/fm/src/lib.rs index fad2634bab4..edf8b8ce8c6 100644 --- a/compiler/fm/src/lib.rs +++ b/compiler/fm/src/lib.rs @@ -134,8 +134,9 @@ impl FileManager { pub trait NormalizePath { /// Replacement for `std::fs::canonicalize` that doesn't verify the path exists. /// - /// Plucked from https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61 - /// Advice from https://www.reddit.com/r/rust/comments/hkkquy/comment/fwtw53s/ + /// Plucked from + /// + /// Advice from fn normalize(&self) -> PathBuf; } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 3712634d707..53e97567703 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -622,7 +622,7 @@ pub const DEFAULT_EXPRESSION_WIDTH: ExpressionWidth = ExpressionWidth::Bounded { /// /// The transformations are _not_ covered by the check that decides whether we can use the cached artifact. /// That comparison is based on on [CompiledProgram::hash] which is a persisted version of the hash of the input -/// [`ast::Program`][noirc_frontend::monomorphization::ast::Program], whereas the output [`circuit::Program`][acir::circuit::Program] +/// [`ast::Program`][noirc_frontend::monomorphization::ast::Program], whereas the output [`circuit::Program`][acvm::acir::circuit::Program] /// contains the final optimized ACIR opcodes, including the transformation done after this compilation. #[tracing::instrument(level = "trace", skip_all, fields(function_name = context.function_name(&main_function)))] pub fn compile_no_check( diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs index 97b200d657a..4ea58bd228b 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs @@ -1,5 +1,5 @@ //! This module analyzes the liveness of variables (non-constant values) throughout a function. -//! It uses the approach detailed in the section 4.2 of this paper https://inria.hal.science/inria-00558509v2/document +//! It uses the approach detailed in the section 4.2 of this paper use crate::ssa::ir::{ basic_block::{BasicBlock, BasicBlockId}, diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index baac70efa24..b5fdf1cc199 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -514,8 +514,10 @@ pub struct PathSegment { impl PathSegment { /// Returns the span where turbofish happen. For example: /// + /// ```noir /// foo:: /// ~^^^^ + /// ``` /// /// Returns an empty span at the end of `foo` if there's no turbofish. pub fn turbofish_span(&self) -> Span { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 064c20538bb..c6e59a57153 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -638,7 +638,7 @@ impl<'context> Elaborator<'context> { } /// Add the given generics to scope. - /// Each generic will have a fresh Shared associated with it. + /// Each generic will have a fresh `Shared` associated with it. pub fn add_generics(&mut self, generics: &UnresolvedGenerics) -> Generics { vecmap(generics, |generic| { let mut is_error = false; diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 63e90e3423c..18eddbb624c 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -185,7 +185,7 @@ impl CrateDefMap { } /// Go through all modules in this crate, and find all functions in - /// each module with the #[export] attribute + /// each module with the `#[export]` attribute pub fn get_all_exported_functions<'a>( &'a self, interner: &'a NodeInterner, diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 0e520700ba3..0891643b48b 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -142,7 +142,7 @@ impl Context<'_, '_> { if parent.is_empty() { name.into() } else { format!("{parent}::{name}") } } - /// Returns a fully-qualified path to the given [StructId] from the given [CrateId]. This function also + /// Returns a fully-qualified path to the given [TypeId] from the given [CrateId]. This function also /// account for the crate names of dependencies. /// /// For example, if you project contains a `main.nr` and `foo.nr` and you provide the `main_crate_id` and the diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 34ee565f7d8..6e11e22a2a2 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -55,7 +55,7 @@ pub enum Type { /// is either a type variable of some kind or a Type::Constant. String(Box), - /// FmtString(N, Vec) is an array of characters of length N that contains + /// `FmtString(N, Vec)` is an array of characters of length N that contains /// a list of fields specified inside the string by the following regular expression r"\{([\S]+)\}" FmtString(Box, Box), diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 791938d1caa..70be13ef7b6 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -645,7 +645,7 @@ impl Token { } /// These are all the operators allowed as part of - /// a short-hand assignment: a = b + /// a short-hand assignment: `a = b` pub fn assign_shorthand_operators() -> [Token; 10] { use Token::*; [Plus, Minus, Star, Slash, Percent, Ampersand, Caret, ShiftLeft, ShiftRight, Pipe] @@ -955,10 +955,10 @@ pub enum SecondaryAttribute { Export, Field(String), - /// A custom tag attribute: #['foo] + /// A custom tag attribute: `#['foo]` Tag(CustomAttribute), - /// An attribute expected to run a comptime function of the same name: #[foo] + /// An attribute expected to run a comptime function of the same name: `#[foo]` Meta(MetaAttribute), Abi(String), diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 88a32b2717c..9b0e1ee8c22 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -1,7 +1,9 @@ //! The noir compiler is separated into the following passes which are listed //! in order in square brackets. The inputs and outputs of each pass are also given: //! +//! ```verbatim //! Source file -[Lexing]-> Tokens -[Parsing]-> Ast -[Name Resolution]-> Hir -[Type Checking]-> Hir -[Monomorphization]-> Monomorphized Ast +//! ``` //! //! After the monomorphized ast is created, it is passed to the noirc_evaluator crate to convert it to SSA form, //! perform optimizations, convert to ACIR and eventually prove/verify the program. diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index aa9ab264198..417f609d9df 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -821,7 +821,7 @@ impl NodeInterner { type_id } - /// Adds [TypeLiasId] and [Location] to the type_alias_ref vector + /// Adds [TypeAliasId] and [Location] to the type_alias_ref vector /// So that we can later resolve [Location]s type aliases from the LSP requests pub fn add_type_alias_ref(&mut self, type_id: TypeAliasId, location: Location) { self.type_alias_ref.push((type_id, location)); diff --git a/compiler/noirc_frontend/src/resolve_locations.rs b/compiler/noirc_frontend/src/resolve_locations.rs index 5946f12db8a..7460ad13c8c 100644 --- a/compiler/noirc_frontend/src/resolve_locations.rs +++ b/compiler/noirc_frontend/src/resolve_locations.rs @@ -11,7 +11,7 @@ impl NodeInterner { /// /// The [Location] may not necessarily point to the beginning of the item /// so we check if the location's span is contained within the start or end - /// of each items [Span] + /// of each items [Location] pub fn find_location_index(&self, location: Location) -> Option> { let mut location_candidate: Option<(&Index, &Location)> = None; @@ -31,7 +31,7 @@ impl NodeInterner { location_candidate.map(|(index, _location)| *index) } - /// Returns the Type of the expression that exists at the given location. + /// Returns the [Type] of the expression that exists at the given location. pub fn type_at_location(&self, location: Location) -> Option<&Type> { // This is similar to `find_location_index` except that we skip indexes for which there is no type let mut location_candidate: Option<(&Index, &Location, &Type)> = None; @@ -52,7 +52,7 @@ impl NodeInterner { location_candidate.map(|(_index, _location, typ)| typ) } - /// Returns the [Location] of the definition of the given Ident found at [Span] of the given [FileId]. + /// Returns the [Location] of the definition of the given Ident found at `location`. /// Returns [None] when definition is not found. pub fn get_definition_location_from( &self, diff --git a/tooling/fuzzer/src/lib.rs b/tooling/fuzzer/src/lib.rs index 471f3da88f6..5eebc67911d 100644 --- a/tooling/fuzzer/src/lib.rs +++ b/tooling/fuzzer/src/lib.rs @@ -1,5 +1,5 @@ //! This module has been adapted from Foundry's fuzzing implementation for the EVM. -//! https://github.com/foundry-rs/foundry/blob/6a85dbaa62f1c305f31cab37781232913055ae28/crates/evm/evm/src/executors/fuzz/mod.rs#L40 +//! //! //! Code is used under the MIT license. diff --git a/tooling/nargo_cli/src/cli/export_cmd.rs b/tooling/nargo_cli/src/cli/export_cmd.rs index 373dfce86a9..fd809c41b18 100644 --- a/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/tooling/nargo_cli/src/cli/export_cmd.rs @@ -22,6 +22,7 @@ use super::check_cmd::check_crate_and_report_errors; use super::{LockType, PackageOptions, WorkspaceCommand}; +#[allow(rustdoc::broken_intra_doc_links)] /// Exports functions marked with #[export] attribute #[derive(Debug, Clone, Args)] pub(crate) struct ExportCommand { diff --git a/tooling/nargo_cli/src/cli/init_cmd.rs b/tooling/nargo_cli/src/cli/init_cmd.rs index bc67a9ba3a8..b88311eb327 100644 --- a/tooling/nargo_cli/src/cli/init_cmd.rs +++ b/tooling/nargo_cli/src/cli/init_cmd.rs @@ -7,6 +7,7 @@ use nargo::package::{CrateName, PackageType}; use noir_artifact_cli::fs::artifact::write_to_file; use std::path::PathBuf; +#[allow(rustdoc::broken_intra_doc_links)] /// Create a Noir project in the current directory. #[derive(Debug, Clone, Args)] pub(crate) struct InitCommand { diff --git a/tooling/nargo_cli/src/cli/lsp_cmd.rs b/tooling/nargo_cli/src/cli/lsp_cmd.rs index 64ca97abc95..24966541cc4 100644 --- a/tooling/nargo_cli/src/cli/lsp_cmd.rs +++ b/tooling/nargo_cli/src/cli/lsp_cmd.rs @@ -13,7 +13,7 @@ use crate::errors::CliError; /// /// Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. /// -/// VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir +/// VS Code Noir Language Support: #[derive(Debug, Clone, Args)] pub(crate) struct LspCommand; diff --git a/tooling/nargo_cli/src/cli/new_cmd.rs b/tooling/nargo_cli/src/cli/new_cmd.rs index 0777ee2d20a..8c8d985bc4f 100644 --- a/tooling/nargo_cli/src/cli/new_cmd.rs +++ b/tooling/nargo_cli/src/cli/new_cmd.rs @@ -5,6 +5,7 @@ use clap::Args; use nargo::package::{CrateName, PackageType}; use std::path::PathBuf; +#[allow(rustdoc::broken_intra_doc_links)] /// Create a Noir project in a new directory. #[derive(Debug, Clone, Args)] pub(crate) struct NewCommand { diff --git a/tooling/nargo_fmt/src/chunks.rs b/tooling/nargo_fmt/src/chunks.rs index 28e6d22f2c8..cb80741e613 100644 --- a/tooling/nargo_fmt/src/chunks.rs +++ b/tooling/nargo_fmt/src/chunks.rs @@ -3,7 +3,7 @@ //! //! It's heavily inspired by this excellent blog post: //! -//! https://yorickpeterse.com/articles/how-to-write-a-code-formatter/ +//! //! //! However, some changes were introduces to handle comments and other particularities of Noir. use std::ops::Deref; diff --git a/tooling/noirc_artifacts/src/contract.rs b/tooling/noirc_artifacts/src/contract.rs index 9f8f7019ff1..3c9fdb1d326 100644 --- a/tooling/noirc_artifacts/src/contract.rs +++ b/tooling/noirc_artifacts/src/contract.rs @@ -65,8 +65,7 @@ impl ContractArtifact { pub struct ContractFunctionArtifact { pub name: String, - /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which the [`ContractFunction`] - /// was compiled. + /// Hash of the monomorphized program from which the [`ContractFunction`] was compiled. #[serde(default)] // For backwards compatibility (it was missing). #[serde(serialize_with = "serialize_hash", deserialize_with = "deserialize_hash")] pub hash: u64, diff --git a/tooling/noirc_artifacts/src/program.rs b/tooling/noirc_artifacts/src/program.rs index fc452fcb2af..f90fc928067 100644 --- a/tooling/noirc_artifacts/src/program.rs +++ b/tooling/noirc_artifacts/src/program.rs @@ -15,8 +15,7 @@ use super::{deserialize_hash, serialize_hash}; pub struct ProgramArtifact { pub noir_version: String, - /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which this [`ProgramArtifact`] - /// was compiled. + /// Hash of the monomorphized program from which this [`ProgramArtifact`] was compiled. /// /// Used to short-circuit compilation in the case of the source code not changing since the last compilation. #[serde(serialize_with = "serialize_hash", deserialize_with = "deserialize_hash")]