From 5f3d7e4f9973c93a8fb42df7bb3e9aebacbf0f88 Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Fri, 7 Mar 2025 10:13:07 +0000 Subject: [PATCH 01/12] Thank you! --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 257fed42304..0e2617a28d7 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,7 @@ Thank you to our sponsors! Gleam would not be possible without you. James MacAulay - Jan Pieper - Jan Skriver Sørensen - + Jean-Adrien Ducastaing - Jean-Luc Geering - Jen Stehlik - jiangplus - @@ -273,7 +274,6 @@ Thank you to our sponsors! Gleam would not be possible without you. Nathaniel Knight - Nayuki - NFIBrokerage - - Nicholas Moen - Nick Chapman - Nick Reynolds - Nicklas Sindlev Andersen - @@ -289,7 +289,6 @@ Thank you to our sponsors! Gleam would not be possible without you. Oliver Medhurst - Oliver Tosky - optizio - - Osman Cea - Patrick Wheeler - Paul Guse - Pawel Biernacki - @@ -361,6 +360,7 @@ Thank you to our sponsors! Gleam would not be possible without you. Travis Johnson - Tristan de Cacqueray - Tristan Sloughter - + Tudor Luca - tymak - upsidedowncake - Valerio Viperino - From e4f5a57fa7c6091dba55bd0e0eb71add82ff13c3 Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Fri, 7 Mar 2025 13:36:48 +0000 Subject: [PATCH 02/12] Update ring to fix vulnerability --- Cargo.lock | 24 +++++++++++++----------- deny.toml | 1 - 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 805f143c3d3..bcc591c49a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,9 +391,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.6" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -2443,15 +2446,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" dependencies = [ "cc", "cfg-if", "getrandom", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2770,6 +2772,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "similar" version = "2.5.0" @@ -2820,12 +2828,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "stable_deref_trait" version = "1.2.0" diff --git a/deny.toml b/deny.toml index 37e20e44c3b..04f49d01911 100644 --- a/deny.toml +++ b/deny.toml @@ -12,7 +12,6 @@ allow = [ "Apache-2.0", "MPL-2.0", "ISC", - "OpenSSL", "CC0-1.0", "BSD-3-Clause", "Unicode-DFS-2016", From 9953119ca8287eaec61bf73a79e5f6b43a6d159b Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Fri, 7 Mar 2025 13:36:04 +0100 Subject: [PATCH 03/12] fix command line name --- compiler-cli/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler-cli/src/lib.rs b/compiler-cli/src/lib.rs index 061922c33c4..ce7d1beb369 100644 --- a/compiler-cli/src/lib.rs +++ b/compiler-cli/src/lib.rs @@ -129,6 +129,7 @@ struct TreeOptions { #[derive(Parser, Debug)] #[command( version, + name = "gleam", next_display_order = None, help_template = "\ {before-help}{name} {version} From f28e5783bb46c8e290012b5a45d99061ea0cb51b Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Fri, 7 Mar 2025 14:53:59 +0100 Subject: [PATCH 04/12] CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cebae8e76d..487569236d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ JavaScript FFI code more compact. They are now one line instead of three. ([Richard Viney](https://github.com/richard-viney)) +### Bug fixes + +- Fixed a bug that would result in displaying the wrong name when running + `gleam --version`. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + ## v1.9.0-rc1 - 2025-03-04 ### Compiler From e41105e205db3d37fd28b9d927d7c9054cf06664 Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Thu, 6 Mar 2025 18:01:56 +0100 Subject: [PATCH 05/12] Fix bug with json encoder generation --- .../src/language_server/code_action.rs | 201 +++++++++++------- .../src/language_server/tests/action.rs | 28 +++ ...with_multiple_variants_with_no_fields.snap | 30 +++ ...on_encoder_for_variant_with_no_fields.snap | 22 ++ 4 files changed, 199 insertions(+), 82 deletions(-) create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_type_with_multiple_variants_with_no_fields.snap create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_variant_with_no_fields.snap diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index 29bff29c697..86dd763ab1a 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -3,10 +3,10 @@ use std::{collections::HashSet, iter, sync::Arc}; use crate::{ Error, STDLIB_PACKAGE_NAME, ast::{ - self, AssignName, AssignmentKind, CallArg, FunctionLiteralKind, ImplicitCallArgOrigin, - Pattern, PipelineAssignmentKind, SrcSpan, TodoKind, TypedArg, TypedAssignment, TypedExpr, - TypedModuleConstant, TypedPattern, TypedPipelineAssignment, TypedRecordConstructor, - TypedStatement, TypedUse, + self, AssignName, AssignmentKind, CallArg, CustomType, FunctionLiteralKind, + ImplicitCallArgOrigin, Pattern, PipelineAssignmentKind, RecordConstructor, SrcSpan, + TodoKind, TypedArg, TypedAssignment, TypedExpr, TypedModuleConstant, TypedPattern, + TypedPipelineAssignment, TypedRecordConstructor, TypedStatement, TypedUse, visit::{Visit as _, visit_typed_call_arg, visit_typed_pattern_call_arg}, }, build::{Located, Module}, @@ -3320,6 +3320,26 @@ pub struct GenerateJsonEncoder<'a> { const JSON_MODULE: &str = "gleam/json"; const JSON_PACKAGE_NAME: &str = "gleam_json"; +#[derive(Eq, PartialEq, Copy, Clone)] +enum EncodingMode { + AsPlainString, + AsObjectWithTypeTag, + AsObjectWithNoTypeTag, +} + +impl EncodingMode { + pub fn for_custom_type(type_: &CustomType>) -> Self { + match type_.constructors.as_slice() { + [constructor] if constructor.arguments.is_empty() => EncodingMode::AsPlainString, + [_constructor] => EncodingMode::AsObjectWithNoTypeTag, + constructors if constructors.iter().all(|c| c.arguments.is_empty()) => { + EncodingMode::AsPlainString + } + _constructors => EncodingMode::AsObjectWithTypeTag, + } + } +} + impl<'a> GenerateJsonEncoder<'a> { pub fn new( module: &'a Module, @@ -3341,45 +3361,98 @@ impl<'a> GenerateJsonEncoder<'a> { self.visit_typed_module(&self.module.ast); } - fn generate_encoder( + fn encoder_body_for_custom_type( &mut self, - constructor: &'a TypedRecordConstructor, + record_name: EcoString, + custom_type: &CustomType>, + ) -> Option { + // We cannot generate a decoder for an external type with no constructors! + let constructors_size = custom_type.constructors.len(); + let (first, rest) = custom_type.constructors.split_first()?; + let mode = EncodingMode::for_custom_type(custom_type); + + // We generate an encoder for a type with a single constructor: it does not + // require pattern matching on the argument as we can access all its fields + // with the usual record access syntax. + if rest.is_empty() { + return self.constructor_encoder(mode, first, custom_type.name.clone(), record_name, 2); + } + + // Otherwise we generate an encoder for a type with multiple constructors: + // it will need to pattern match on the various constructors and encode each + // one separately. + let mut branches = Vec::with_capacity(constructors_size); + for constructor in iter::once(first).chain(rest) { + let RecordConstructor { name, .. } = constructor; + let encoder = self.constructor_encoder( + mode, + constructor, + custom_type.name.clone(), + record_name.clone(), + 4, + )?; + let unpacking = if constructor.arguments.is_empty() { + "" + } else { + "(..)" + }; + branches.push(eco_format!(" {name}{unpacking} -> {encoder}")); + } + + let branches = branches.join("\n"); + Some(eco_format!( + "case {record_name} {{ +{branches} + }}", + )) + } + + fn constructor_encoder( + &mut self, + mode: EncodingMode, + constructor: &TypedRecordConstructor, type_name: EcoString, record_name: EcoString, - variant_tag: Option, - indent: usize, + nesting: usize, ) -> Option { - let fields = constructor - .arguments - .iter() - .map(|argument| { - Some(RecordField { - label: RecordLabel::Labeled( - argument.label.as_ref().map(|(_, name)| name.as_str())?, - ), - type_: &argument.type_, - }) - }) - .collect::>>()?; + let json_module = self.printer.print_module(JSON_MODULE); + let tag = constructor.name.to_snake_case(); + let indent = " ".repeat(nesting); + // If the variant is encoded as a simple json string we just call the + // `json.string` with the variant tag as an argument. + if mode == EncodingMode::AsPlainString { + return Some(eco_format!("{json_module}.string(\"{tag}\")")); + } + + // Otherwise we turn it into an object with a `type` tag field. let mut encoder_printer = EncoderPrinter::new(&self.module.ast.names, type_name, self.module.name.clone()); - let encoders = fields - .iter() - .map(|field| encoder_printer.encode_field(&record_name, field, indent + 2)) - .join(",\n"); + // These aare the fields of the json object to encode. + let mut fields = Vec::with_capacity(constructor.arguments.len()); + if mode == EncodingMode::AsObjectWithTypeTag { + // Any needed type tag is always going to be the first field in the object + fields.push(eco_format!( + "{indent} #(\"type\", {json_module}.string(\"{tag}\"))" + )); + } - let indent = " ".repeat(indent); - let json_module = self.printer.print_module(JSON_MODULE); + for argument in constructor.arguments.iter() { + let (_, label) = argument.label.as_ref()?; + let field = RecordField { + label: RecordLabel::Labeled(label), + type_: &argument.type_, + }; + let encoder = encoder_printer.encode_field(&record_name, &field, nesting + 2); + fields.push(encoder); + } + let fields = fields.join(&format!(",\n")); Some(eco_format!( - "{json_module}.object([{tag} -{encoders}, -{indent}])", - tag = variant_tag - .map(|tag| eco_format!("\n{indent} #(\"type\", {json_module}.string(\"{tag}\")),")) - .unwrap_or_default() + "{json_module}.object([ +{fields}, +{indent}])" )) } } @@ -3390,48 +3463,14 @@ impl<'ast> ast::visit::Visit<'ast> for GenerateJsonEncoder<'ast> { if !overlaps(self.params.range, range) { return; } - let record_name: EcoString = custom_type.name.to_snake_case().into(); - let Some(encoder) = (match custom_type.constructors.as_slice() { - // We can't generate an encoder for an external type - [] => return, - [constructor] => self.generate_encoder( - constructor, - custom_type.name.clone(), - record_name.clone(), - None, - 2, - ), - constructors => constructors - .iter() - .map(|constructor| { - Some(eco_format!( - " {name}(..) -> {encoder}", - name = constructor.name, - encoder = self.generate_encoder( - constructor, - custom_type.name.clone(), - record_name.clone(), - Some(constructor.name.to_snake_case().into()), - 4 - )? - )) - }) - .collect::>>() - .map(|cases| { - eco_format!( - "case {record_name} {{ -{cases} - }}", - cases = cases.join("\n") - ) - }), - }) else { + let record_name = EcoString::from(custom_type.name.to_snake_case()); + let name = eco_format!("encode_{record_name}"); + let Some(encoder) = self.encoder_body_for_custom_type(record_name.clone(), custom_type) + else { return; }; - let name = eco_format!("encode_{record_name}"); - let json_type = self.printer.print_type(&Type::Named { publicity: ast::Publicity::Public, package: JSON_PACKAGE_NAME.into(), @@ -3441,25 +3480,23 @@ impl<'ast> ast::visit::Visit<'ast> for GenerateJsonEncoder<'ast> { inferred_variant: None, }); - let parameters = match custom_type.parameters.len() { - 0 => EcoString::new(), - _ => eco_format!( - "({})", - custom_type - .parameters - .iter() - .map(|(_, name)| name) - .join(", ") - ), + let type_ = if custom_type.parameters.is_empty() { + custom_type.name.clone() + } else { + let parameters = custom_type + .parameters + .iter() + .map(|(_, name)| name) + .join(", "); + eco_format!("{}({})", custom_type.name, parameters) }; let function = format!( " -fn {name}({record_name}: {type_name}{parameters}) -> {json_type} {{ +fn {name}({record_name}: {type_}) -> {json_type} {{ {encoder} }}", - type_name = custom_type.name, ); self.edits.insert(custom_type.end_position, function); diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 7de98c019fa..76686a1f3ee 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -5202,6 +5202,34 @@ pub type Wibble { ); } +#[test] +fn generate_json_encoder_for_variant_with_no_fields() { + assert_code_action!( + GENERATE_JSON_ENCODER, + " +pub type Wibble { + Wibble +} +", + find_position_of("type W").to_selection() + ); +} + +#[test] +fn generate_json_encoder_for_type_with_multiple_variants_with_no_fields() { + assert_code_action!( + GENERATE_JSON_ENCODER, + " +pub type Wibble { + Wibble + Wobble + Woo +} +", + find_position_of("type W").to_selection() + ); +} + #[test] fn convert_to_function_call_works_with_function_producing_another_function() { assert_code_action!( diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_type_with_multiple_variants_with_no_fields.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_type_with_multiple_variants_with_no_fields.snap new file mode 100644 index 00000000000..004346f4e36 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_type_with_multiple_variants_with_no_fields.snap @@ -0,0 +1,30 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub type Wibble {\n Wibble\n Wobble\n Woo\n}\n" +--- +----- BEFORE ACTION + +pub type Wibble { + ↑ + Wibble + Wobble + Woo +} + + +----- AFTER ACTION +import gleam/json + +pub type Wibble { + Wibble + Wobble + Woo +} + +fn encode_wibble(wibble: Wibble) -> json.Json { + case wibble { + Wibble -> json.string("wibble") + Wobble -> json.string("wobble") + Woo -> json.string("woo") + } +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_variant_with_no_fields.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_variant_with_no_fields.snap new file mode 100644 index 00000000000..5ef30d086bb --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_variant_with_no_fields.snap @@ -0,0 +1,22 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub type Wibble {\n Wibble\n}\n" +--- +----- BEFORE ACTION + +pub type Wibble { + ↑ + Wibble +} + + +----- AFTER ACTION +import gleam/json + +pub type Wibble { + Wibble +} + +fn encode_wibble(wibble: Wibble) -> json.Json { + json.string("wibble") +} From 7e026eb194eff37f9b30eeb6acc4213635f95386 Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Thu, 6 Mar 2025 23:57:54 +0100 Subject: [PATCH 06/12] Add another test --- .../src/language_server/tests/action.rs | 14 ++++++++ ...ncoder_for_variants_with_mixed_fields.snap | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_variants_with_mixed_fields.snap diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 76686a1f3ee..59f089ed3b5 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -5230,6 +5230,20 @@ pub type Wibble { ); } +#[test] +fn generate_json_encoder_for_variants_with_mixed_fields() { + assert_code_action!( + GENERATE_JSON_ENCODER, + " +pub type Wibble { + Wibble + Wobble(field: String, field1: Int) +} +", + find_position_of("type W").to_selection() + ); +} + #[test] fn convert_to_function_call_works_with_function_producing_another_function() { assert_code_action!( diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_variants_with_mixed_fields.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_variants_with_mixed_fields.snap new file mode 100644 index 00000000000..d0ac3c197c9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_json_encoder_for_variants_with_mixed_fields.snap @@ -0,0 +1,33 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub type Wibble {\n Wibble\n Wobble(field: String, field1: Int)\n}\n" +--- +----- BEFORE ACTION + +pub type Wibble { + ↑ + Wibble + Wobble(field: String, field1: Int) +} + + +----- AFTER ACTION +import gleam/json + +pub type Wibble { + Wibble + Wobble(field: String, field1: Int) +} + +fn encode_wibble(wibble: Wibble) -> json.Json { + case wibble { + Wibble -> json.object([ + #("type", json.string("wibble")), + ]) + Wobble(..) -> json.object([ + #("type", json.string("wobble")), + #("field", json.string(wibble.field)), + #("field1", json.int(wibble.field1)), + ]) + } +} From b32caf4c26954c23b365db3f2018cc9306f3fb11 Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Thu, 6 Mar 2025 23:58:21 +0100 Subject: [PATCH 07/12] fix generate dynamic decoder --- .../src/language_server/code_action.rs | 153 ++++++++++-------- .../src/language_server/tests/action.rs | 42 +++++ ...ic_decoder_for_variant_with_no_fields.snap | 26 +++ ...ecoder_for_variants_with_mixed_fields.snap | 33 ++++ ...c_decoder_for_variants_with_no_fields.snap | 32 ++++ 5 files changed, 221 insertions(+), 65 deletions(-) create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variant_with_no_fields.snap create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variants_with_mixed_fields.snap create mode 100644 compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variants_with_no_fields.snap diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index 86dd763ab1a..61c0f464735 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -2995,24 +2995,78 @@ impl<'a> GenerateDynamicDecoder<'a> { self.visit_typed_module(&self.module.ast); } - fn generate_decoder_for_variant( + fn custom_type_decoder_body( &mut self, - custom_type: &'a ast::TypedCustomType, - constructor: &'a TypedRecordConstructor, - indent: usize, + custom_type: &CustomType>, ) -> Option { - let fields = constructor - .arguments - .iter() - .map(|argument| { - Some(RecordField { - label: RecordLabel::Labeled( - argument.label.as_ref().map(|(_, name)| name.as_str())?, - ), - type_: &argument.type_, - }) - }) - .collect::>>()?; + // We cannot generate a decoder for an external type with no constructors! + let constructors_size = custom_type.constructors.len(); + let (first, rest) = custom_type.constructors.split_first()?; + let mode = EncodingMode::for_custom_type(custom_type); + + // We generate a decoder for a type with a single constructor: it does not + // require pattern matching on a tag as there's no variants to tell apart. + if rest.is_empty() && mode == EncodingMode::AsObjectWithNoTypeTag { + return self.constructor_decoder(mode, custom_type, first, 0); + } + + // Otherwise we need to generate a decoder that has to tell apart different + // variants, depending on the mode we might have to decode a type field or + // plain strings! + let module = self.printer.print_module(DECODE_MODULE); + let discriminant = if mode == EncodingMode::AsPlainString { + eco_format!("use variant <- {module}.then({module}.string)") + } else { + eco_format!("use variant <- {module}.field(\"type\", {module}.string)") + }; + + let mut branches = Vec::with_capacity(constructors_size); + for constructor in iter::once(first).chain(rest) { + let body = self.constructor_decoder(mode, custom_type, constructor, 4)?; + let name = constructor.name.to_snake_case(); + branches.push(eco_format!(r#" "{name}" -> {body}"#)); + } + + let cases = branches.join("\n"); + let type_name = &custom_type.name; + Some(eco_format!( + r#"{{ + {discriminant} + case variant {{ +{cases} + _ -> {module}.failure(todo as "Zero value for {type_name}", "{type_name}") + }} +}}"#, + )) + } + + fn constructor_decoder( + &mut self, + mode: EncodingMode, + custom_type: &ast::TypedCustomType, + constructor: &TypedRecordConstructor, + nesting: usize, + ) -> Option { + let decode_module = self.printer.print_module(DECODE_MODULE); + let constructor_name = &constructor.name; + + // If the constructor was encoded as a plain string with no additional + // fields it means there's nothing else to decode and we can just + // succeed. + if mode == EncodingMode::AsPlainString { + return Some(eco_format!("{decode_module}.success({constructor_name})")); + } + + // Otherwise we have to decode all the constructor fields to build it. + let mut fields = Vec::with_capacity(constructor.arguments.len()); + for argument in constructor.arguments.iter() { + let (_, name) = argument.label.as_ref()?; + let field = RecordField { + label: RecordLabel::Labeled(name), + type_: &argument.type_, + }; + fields.push(field); + } let mut decoder_printer = DecoderPrinter::new( &self.module.ast.names, @@ -3022,21 +3076,26 @@ impl<'a> GenerateDynamicDecoder<'a> { let decoders = fields .iter() - .map(|field| decoder_printer.decode_field(field, indent + 2)) + .map(|field| decoder_printer.decode_field(field, nesting + 2)) .join("\n"); - let decode_module = self.printer.print_module(DECODE_MODULE); - let mut field_names = fields.iter().map(|field| field.label.variable_name()); + let indent = " ".repeat(nesting); - Some(eco_format!( - "{{ + Some(if decoders.is_empty() { + eco_format!("{decode_module}.success({constructor_name})") + } else { + let field_names = fields + .iter() + .map(|field| format!("{}:", field.label.variable_name())) + .join(", "); + + eco_format!( + "{{ {decoders} -{indent} {decode_module}.success({constructor_name}({fields}:)) +{indent} {decode_module}.success({constructor_name}({field_names})) {indent}}}", - constructor_name = constructor.name, - fields = field_names.join(":, "), - indent = " ".repeat(indent), - )) + ) + }) } } @@ -3048,42 +3107,7 @@ impl<'ast> ast::visit::Visit<'ast> for GenerateDynamicDecoder<'ast> { } let name = eco_format!("{}_decoder", custom_type.name.to_snake_case()); - - let Some(function_body) = (match custom_type.constructors.as_slice() { - // We can't generate a decoder for an external type - [] => return, - [constructor] => self.generate_decoder_for_variant(custom_type, constructor, 0), - constructors => { - let Some(cases) = constructors - .iter() - .map(|constructor| { - self.generate_decoder_for_variant(custom_type, constructor, 4) - .map(|body| { - eco_format!( - r#" "{name}" -> {body}"#, - name = constructor.name.to_snake_case() - ) - }) - }) - .collect::>>() - else { - return; - }; - - let module = self.printer.print_module(DECODE_MODULE); - Some(eco_format!( - r#"{{ - use variant <- {module}.field("type", {module}.string) - case variant {{ -{cases} - _ -> {module}.failure(todo as "Zero value for {type_name}", "{type_name}") - }} -}}"#, - type_name = custom_type.name, - cases = cases.join("\n") - )) - } - }) else { + let Some(function_body) = self.custom_type_decoder_body(custom_type) else { return; }; @@ -3361,7 +3385,7 @@ impl<'a> GenerateJsonEncoder<'a> { self.visit_typed_module(&self.module.ast); } - fn encoder_body_for_custom_type( + fn custom_type_encoder_body( &mut self, record_name: EcoString, custom_type: &CustomType>, @@ -3466,8 +3490,7 @@ impl<'ast> ast::visit::Visit<'ast> for GenerateJsonEncoder<'ast> { let record_name = EcoString::from(custom_type.name.to_snake_case()); let name = eco_format!("encode_{record_name}"); - let Some(encoder) = self.encoder_body_for_custom_type(record_name.clone(), custom_type) - else { + let Some(encoder) = self.custom_type_encoder_body(record_name.clone(), custom_type) else { return; }; diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index 59f089ed3b5..63b20ea9a4d 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -4542,6 +4542,48 @@ pub type Wibble { ); } +#[test] +fn generate_dynamic_decoder_for_variant_with_no_fields() { + assert_code_action!( + GENERATE_DYNAMIC_DECODER, + " +pub type Wibble { + Wibble +} +", + find_position_of("type").to_selection() + ); +} + +#[test] +fn generate_dynamic_decoder_for_variants_with_no_fields() { + assert_code_action!( + GENERATE_DYNAMIC_DECODER, + " +pub type Wibble { + Wibble + Wobble + Woo +} +", + find_position_of("type").to_selection() + ); +} + +#[test] +fn generate_dynamic_decoder_for_variants_with_mixed_fields() { + assert_code_action!( + GENERATE_DYNAMIC_DECODER, + " +pub type Wibble { + Wibble + Wobble(field: String, field2: Int) +} +", + find_position_of("type").to_selection() + ); +} + #[test] fn no_code_action_to_generate_dynamic_decoder_for_type_without_labels() { assert_no_code_actions!( diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variant_with_no_fields.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variant_with_no_fields.snap new file mode 100644 index 00000000000..5fdd415001d --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variant_with_no_fields.snap @@ -0,0 +1,26 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub type Wibble {\n Wibble\n}\n" +--- +----- BEFORE ACTION + +pub type Wibble { + ↑ + Wibble +} + + +----- AFTER ACTION +import gleam/dynamic/decode + +pub type Wibble { + Wibble +} + +fn wibble_decoder() -> decode.Decoder(Wibble) { + use variant <- decode.then(decode.string) + case variant { + "wibble" -> decode.success(Wibble) + _ -> decode.failure(todo as "Zero value for Wibble", "Wibble") + } +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variants_with_mixed_fields.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variants_with_mixed_fields.snap new file mode 100644 index 00000000000..e339988e663 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variants_with_mixed_fields.snap @@ -0,0 +1,33 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub type Wibble {\n Wibble\n Wobble(field: String, field2: Int)\n}\n" +--- +----- BEFORE ACTION + +pub type Wibble { + ↑ + Wibble + Wobble(field: String, field2: Int) +} + + +----- AFTER ACTION +import gleam/dynamic/decode + +pub type Wibble { + Wibble + Wobble(field: String, field2: Int) +} + +fn wibble_decoder() -> decode.Decoder(Wibble) { + use variant <- decode.field("type", decode.string) + case variant { + "wibble" -> decode.success(Wibble) + "wobble" -> { + use field <- decode.field("field", decode.string) + use field2 <- decode.field("field2", decode.int) + decode.success(Wobble(field:, field2:)) + } + _ -> decode.failure(todo as "Zero value for Wibble", "Wibble") + } +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variants_with_no_fields.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variants_with_no_fields.snap new file mode 100644 index 00000000000..e285a10f44e --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__generate_dynamic_decoder_for_variants_with_no_fields.snap @@ -0,0 +1,32 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub type Wibble {\n Wibble\n Wobble\n Woo\n}\n" +--- +----- BEFORE ACTION + +pub type Wibble { + ↑ + Wibble + Wobble + Woo +} + + +----- AFTER ACTION +import gleam/dynamic/decode + +pub type Wibble { + Wibble + Wobble + Woo +} + +fn wibble_decoder() -> decode.Decoder(Wibble) { + use variant <- decode.then(decode.string) + case variant { + "wibble" -> decode.success(Wibble) + "wobble" -> decode.success(Wobble) + "woo" -> decode.success(Woo) + _ -> decode.failure(todo as "Zero value for Wibble", "Wibble") + } +} From 9f6f533ea1cc449197d7a48fad95dadf37f4f9c6 Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Fri, 7 Mar 2025 00:02:22 +0100 Subject: [PATCH 08/12] clippy --- .../src/language_server/code_action.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index 61c0f464735..89926504621 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -3006,7 +3006,7 @@ impl<'a> GenerateDynamicDecoder<'a> { // We generate a decoder for a type with a single constructor: it does not // require pattern matching on a tag as there's no variants to tell apart. - if rest.is_empty() && mode == EncodingMode::AsObjectWithNoTypeTag { + if rest.is_empty() && mode == EncodingMode::ObjectWithNoTypeTag { return self.constructor_decoder(mode, custom_type, first, 0); } @@ -3014,7 +3014,7 @@ impl<'a> GenerateDynamicDecoder<'a> { // variants, depending on the mode we might have to decode a type field or // plain strings! let module = self.printer.print_module(DECODE_MODULE); - let discriminant = if mode == EncodingMode::AsPlainString { + let discriminant = if mode == EncodingMode::PlainString { eco_format!("use variant <- {module}.then({module}.string)") } else { eco_format!("use variant <- {module}.field(\"type\", {module}.string)") @@ -3053,7 +3053,7 @@ impl<'a> GenerateDynamicDecoder<'a> { // If the constructor was encoded as a plain string with no additional // fields it means there's nothing else to decode and we can just // succeed. - if mode == EncodingMode::AsPlainString { + if mode == EncodingMode::PlainString { return Some(eco_format!("{decode_module}.success({constructor_name})")); } @@ -3346,20 +3346,20 @@ const JSON_PACKAGE_NAME: &str = "gleam_json"; #[derive(Eq, PartialEq, Copy, Clone)] enum EncodingMode { - AsPlainString, - AsObjectWithTypeTag, - AsObjectWithNoTypeTag, + PlainString, + ObjectWithTypeTag, + ObjectWithNoTypeTag, } impl EncodingMode { pub fn for_custom_type(type_: &CustomType>) -> Self { match type_.constructors.as_slice() { - [constructor] if constructor.arguments.is_empty() => EncodingMode::AsPlainString, - [_constructor] => EncodingMode::AsObjectWithNoTypeTag, + [constructor] if constructor.arguments.is_empty() => EncodingMode::PlainString, + [_constructor] => EncodingMode::ObjectWithNoTypeTag, constructors if constructors.iter().all(|c| c.arguments.is_empty()) => { - EncodingMode::AsPlainString + EncodingMode::PlainString } - _constructors => EncodingMode::AsObjectWithTypeTag, + _constructors => EncodingMode::ObjectWithTypeTag, } } } @@ -3445,7 +3445,7 @@ impl<'a> GenerateJsonEncoder<'a> { // If the variant is encoded as a simple json string we just call the // `json.string` with the variant tag as an argument. - if mode == EncodingMode::AsPlainString { + if mode == EncodingMode::PlainString { return Some(eco_format!("{json_module}.string(\"{tag}\")")); } @@ -3455,7 +3455,7 @@ impl<'a> GenerateJsonEncoder<'a> { // These aare the fields of the json object to encode. let mut fields = Vec::with_capacity(constructor.arguments.len()); - if mode == EncodingMode::AsObjectWithTypeTag { + if mode == EncodingMode::ObjectWithTypeTag { // Any needed type tag is always going to be the first field in the object fields.push(eco_format!( "{indent} #(\"type\", {json_module}.string(\"{tag}\"))" @@ -3472,7 +3472,7 @@ impl<'a> GenerateJsonEncoder<'a> { fields.push(encoder); } - let fields = fields.join(&format!(",\n")); + let fields = fields.join(",\n"); Some(eco_format!( "{json_module}.object([ {fields}, From 4bfb95ca38fa1ebe3ebe7cb25d7d6d75b905183f Mon Sep 17 00:00:00 2001 From: Giacomo Cavalieri Date: Fri, 7 Mar 2025 14:52:03 +0100 Subject: [PATCH 09/12] CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 487569236d4..4ef8ae6ea48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ `gleam --version`. ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) +- Fixed a bug in the `generate json encoder` and `generate dynamic decoder` that + would result in generating invalid code for variants with no fields. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + ## v1.9.0-rc1 - 2025-03-04 ### Compiler From e3dd8c9258d52306a866beb5413f02423a9d9cd7 Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Fri, 7 Mar 2025 20:35:37 +0000 Subject: [PATCH 10/12] Push lockfile --- Cargo.lock | 42 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bcc591c49a7..04f437c4371 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -638,7 +638,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", - "hashbrown 0.14.3", + "hashbrown", "lock_api", "once_cell", "parking_lot_core", @@ -1169,12 +1169,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.3" @@ -1613,16 +1607,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.2.6" @@ -1630,7 +1614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -2050,7 +2034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.2.6", + "indexmap", ] [[package]] @@ -2157,9 +2141,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.4.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58678a64de2fced2bdec6bca052a6716a0efe692d6e3f53d1bda6a1def64cfc0" +checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" dependencies = [ "once_cell", "protobuf-support", @@ -2168,9 +2152,9 @@ dependencies = [ [[package]] name = "protobuf-codegen" -version = "3.4.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32777b0b3f6538d9d2e012b3fad85c7e4b9244b5958d04a6415f4333782b7a77" +checksum = "e26b833f144769a30e04b1db0146b2aaa53fd2fd83acf10a6b5f996606c18144" dependencies = [ "anyhow", "once_cell", @@ -2183,12 +2167,12 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.4.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cb37955261126624a25b5e6bda40ae34cf3989d52a783087ca6091b29b5642" +checksum = "322330e133eab455718444b4e033ebfac7c6528972c784fcde28d2cc783c6257" dependencies = [ "anyhow", - "indexmap 1.9.3", + "indexmap", "log", "protobuf", "protobuf-support", @@ -2199,9 +2183,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.4.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ed294a835b0f30810e13616b1cd34943c6d1e84a8f3b0dcfe466d256c3e7e7" +checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" dependencies = [ "thiserror", ] @@ -3149,7 +3133,7 @@ version = "0.22.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" dependencies = [ - "indexmap 2.2.6", + "indexmap", "toml_datetime", "winnow", ] From 31ab79d500adfa87645ea6e294c8fd8698a5eb7d Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Fri, 7 Mar 2025 20:41:34 +0000 Subject: [PATCH 11/12] Update hexpm --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04f437c4371..33a17b2a5a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1189,9 +1189,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hexpm" -version = "2.4.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555eed2a876b665de8e13dca9ac08108b95d837ef3d7fe80205531e42eaf594b" +checksum = "34e761adec4b86deeb80dba70eef8a9d3b7f8d9165047974659d0069fde5ee57" dependencies = [ "base16", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 10ce87218ec..144b9a91a6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ walkdir = "2" # Enum trait impl macros strum = { version = "0", features = ["derive"] } # Hex package manager client -hexpm = "2" +hexpm = "3" # Creation of tar file archives tar = "0" # gzip compression From 429d3b10d7a47336fe7ff39fe11ef84f8cbfcf3b Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Fri, 7 Mar 2025 20:56:17 +0000 Subject: [PATCH 12/12] v1.9.0-rc2 --- CHANGELOG.md | 2 +- Cargo.lock | 14 +++++++------- compiler-cli/Cargo.toml | 2 +- compiler-core/Cargo.toml | 2 +- compiler-wasm/Cargo.toml | 2 +- gleam-bin/Cargo.toml | 2 +- test-output/Cargo.toml | 2 +- test-package-compiler/Cargo.toml | 2 +- test-project-compiler/Cargo.toml | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ef8ae6ea48..c1613f003ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## v1.9.0-rc2 - 2025-03-07 ### Compiler diff --git a/Cargo.lock b/Cargo.lock index 33a17b2a5a5..b68f605e8be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1026,7 +1026,7 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "gleam" -version = "1.9.0-rc1" +version = "1.9.0-rc2" dependencies = [ "gleam-cli", "static_vcruntime", @@ -1034,7 +1034,7 @@ dependencies = [ [[package]] name = "gleam-cli" -version = "1.9.0-rc1" +version = "1.9.0-rc2" dependencies = [ "async-trait", "base16", @@ -1082,7 +1082,7 @@ dependencies = [ [[package]] name = "gleam-core" -version = "1.9.0-rc1" +version = "1.9.0-rc2" dependencies = [ "age", "askama", @@ -1137,7 +1137,7 @@ dependencies = [ [[package]] name = "gleam-wasm" -version = "1.9.0-rc1" +version = "1.9.0-rc2" dependencies = [ "camino", "console_error_panic_hook", @@ -2961,7 +2961,7 @@ dependencies = [ [[package]] name = "test-output" -version = "1.9.0-rc1" +version = "1.9.0-rc2" dependencies = [ "camino", "gleam-cli", @@ -2972,7 +2972,7 @@ dependencies = [ [[package]] name = "test-package-compiler" -version = "1.9.0-rc1" +version = "1.9.0-rc2" dependencies = [ "camino", "gleam-core", @@ -2987,7 +2987,7 @@ dependencies = [ [[package]] name = "test-project-compiler" -version = "1.9.0-rc1" +version = "1.9.0-rc2" dependencies = [ "camino", "gleam-core", diff --git a/compiler-cli/Cargo.toml b/compiler-cli/Cargo.toml index b39eede84f6..c1f02307205 100644 --- a/compiler-cli/Cargo.toml +++ b/compiler-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gleam-cli" -version = "1.9.0-rc1" +version = "1.9.0-rc2" authors = ["Louis Pilfold "] edition = "2024" license-file = "LICENCE" diff --git a/compiler-core/Cargo.toml b/compiler-core/Cargo.toml index ef519377e89..ac5999cab21 100644 --- a/compiler-core/Cargo.toml +++ b/compiler-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gleam-core" -version = "1.9.0-rc1" +version = "1.9.0-rc2" authors = ["Louis Pilfold "] edition = "2024" license-file = "LICENCE" diff --git a/compiler-wasm/Cargo.toml b/compiler-wasm/Cargo.toml index 522faa3ec1b..c3ee0e46e4c 100644 --- a/compiler-wasm/Cargo.toml +++ b/compiler-wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gleam-wasm" -version = "1.9.0-rc1" +version = "1.9.0-rc2" authors = ["Louis Pilfold "] edition = "2024" license-file = "LICENCE" diff --git a/gleam-bin/Cargo.toml b/gleam-bin/Cargo.toml index 8de12aa227d..04f20f45383 100644 --- a/gleam-bin/Cargo.toml +++ b/gleam-bin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gleam" -version = "1.9.0-rc1" +version = "1.9.0-rc2" authors = ["Louis Pilfold "] edition = "2024" license-file = "LICENCE" diff --git a/test-output/Cargo.toml b/test-output/Cargo.toml index f5065f1c5fc..107f9184f7f 100644 --- a/test-output/Cargo.toml +++ b/test-output/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-output" -version = "1.9.0-rc1" +version = "1.9.0-rc2" authors = ["Louis Pilfold "] edition = "2024" license = "Apache-2.0" diff --git a/test-package-compiler/Cargo.toml b/test-package-compiler/Cargo.toml index 4aaa2225df5..07ab4133d37 100644 --- a/test-package-compiler/Cargo.toml +++ b/test-package-compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-package-compiler" -version = "1.9.0-rc1" +version = "1.9.0-rc2" authors = ["Louis Pilfold "] edition = "2024" license = "Apache-2.0" diff --git a/test-project-compiler/Cargo.toml b/test-project-compiler/Cargo.toml index 2377422419b..748d0c85b6a 100644 --- a/test-project-compiler/Cargo.toml +++ b/test-project-compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-project-compiler" -version = "1.9.0-rc1" +version = "1.9.0-rc2" authors = ["Louis Pilfold "] edition = "2024" license = "Apache-2.0"