Repository: gleam-lang/gleam (rust)

Issue:
"Inline Variable" action does not work with leading field lookups
Bug with the "inline variable" code refactor action. When in-lining a variable, if the definition body of the variable **starts by referencing a field** then the action breaks the code, by omitting the record.

## main example
```
type Foo {
  Foo(items: List(Int))
}

pub fn main() {
  let myfoo = Foo([])
  let xyz = myfoo.items       // reference any `record.field`, or bigger expressions starting like this
  echo xyz
}
```
In-lining the variable xyz results in this:
```
pub fn main() {
  let myfoo = Foo([])
  echo items                  // expected "echo myfoo.items"
}
```
## what works okay
Wrapping in braces works okay:
```
  let myfoo = Foo([])
  let xyz = {myfoo.items} 
  echo xyz
```
Leading with not a field reference works okay:
```
  let myfoo = Foo([])
  let item = list.first(myfoo.items) // fine leading with a module reference
  echo item
```


## Versions

Gleam 1.9.1

VsCode
Version: 1.98.2 (user setup)
Commit: ddc367ed5c8936efe395cffeec279b04ffd7db78
Date: 2025-03-12T13:32:45.399Z
Electron: 34.2.0
ElectronBuildId: 11161602
Chromium: 132.0.6834.196
Node.js: 20.18.2
V8: 13.2.152.36-electron.0
OS: Windows_NT x64 10.0.26100

Pull request:
Use correct location when inlining field access variable
This PR closes #4430
The field access location now spans the entire thing and is consistent with the way we've updated the module select location a while ago.

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 30b3f516e..09d6c05f1 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -480,7 +480,7 @@ jobs:
         working-directory: ./test/hextarball
 
       - name: Test running modules
-        run: make test
+        run: make test-all
         working-directory: ./test/running_modules
 
       - name: test/multi_namespace
diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs
index d8bb9d77c..b9728547b 100644
--- a/compiler-cli/src/dependencies.rs
+++ b/compiler-cli/src/dependencies.rs
@@ -1014,7 +1014,7 @@ fn provide_package(
     if parents.contains(&package_name) {
         let mut last_cycle = parents
             .split(|p| p == &package_name)
-            .last()
+            .next_back()
             .unwrap_or_default()
             .to_vec();
         last_cycle.push(package_name);
diff --git a/compiler-core/src/ast.rs b/compiler-core/src/ast.rs
index 89f4ba126..6b27c066f 100644
--- a/compiler-core/src/ast.rs
+++ b/compiler-core/src/ast.rs
@@ -726,7 +726,7 @@ impl<T> Import<T> {
         match self.as_name.as_ref() {
             Some((AssignName::Variable(name), _)) => Some(name.clone()),
             Some((AssignName::Discard(_), _)) => None,
-            None => self.module.split('/').last().map(EcoString::from),
+            None => self.module.split('/').next_back().map(EcoString::from),
         }
     }
 
diff --git a/compiler-core/src/ast/typed.rs b/compiler-core/src/ast/typed.rs
index 4c8594ecd..4877bfab3 100644
--- a/compiler-core/src/ast/typed.rs
+++ b/compiler-core/src/ast/typed.rs
@@ -90,6 +90,7 @@ pub enum TypedExpr {
 
     RecordAccess {
         location: SrcSpan,
+        field_start: u32,
         type_: Arc<Type>,
         label: EcoString,
         index: u64,
diff --git a/compiler-core/src/ast/visit.rs b/compiler-core/src/ast/visit.rs
index 3daf6c180..d8990d5e6 100644
--- a/compiler-core/src/ast/visit.rs
+++ b/compiler-core/src/ast/visit.rs
@@ -213,12 +213,13 @@ pub trait Visit<'ast> {
     fn visit_typed_expr_record_access(
         &mut self,
         location: &'ast SrcSpan,
+        field_start: &'ast u32,
         type_: &'ast Arc<Type>,
         label: &'ast EcoString,
         index: &'ast u64,
         record: &'ast TypedExpr,
     ) {
-        visit_typed_expr_record_access(self, location, type_, label, index, record);
+        visit_typed_expr_record_access(self, location, field_start, type_, label, index, record);
     }
 
     #[allow(clippy::too_many_arguments)]
@@ -770,11 +771,12 @@ where
         } => v.visit_typed_expr_case(location, type_, subjects, clauses),
         TypedExpr::RecordAccess {
             location,
+            field_start,
             type_,
             label,
             index,
             record,
-        } => v.visit_typed_expr_record_access(location, type_, label, index, record),
+        } => v.visit_typed_expr_record_access(location, field_start, type_, label, index, record),
         TypedExpr::ModuleSelect {
             location,
             field_start,
@@ -1001,6 +1003,7 @@ pub fn visit_typed_expr_case<'a, V>(
 pub fn visit_typed_expr_record_access<'a, V>(
     v: &mut V,
     _location: &'a SrcSpan,
+    _field_start: &'a u32,
     _type_: &'a Arc<Type>,
     _label: &'a EcoString,
     _index: &'a u64,
diff --git a/compiler-core/src/dependency.rs b/compiler-core/src/dependency.rs
index 3d2f810fd..c6b58cd3b 100644
--- a/compiler-core/src/dependency.rs
+++ b/compiler-core/src/dependency.rs
@@ -72,11 +72,7 @@ fn parse_exact_version(ver: &str) -> Option<Version> {
     if version.starts_with("==") || first_byte.is_some_and(|v| v.is_ascii_digit()) {
         let version = version.replace("==", "");
         let version = version.as_str().trim();
-        if let Ok(v) = Version::parse(version) {
-            Some(v)
-        } else {
-            None
-        }
+        Version::parse(version).ok()
     } else {
         None
     }
diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs
index cad477f6a..eceaa0c3c 100644
--- a/compiler-core/src/error.rs
+++ b/compiler-core/src/error.rs
@@ -789,7 +789,7 @@ fn edit_distance_with_substrings(a: &str, b: &str, limit: usize) -> Option<usize
         1 // Exact substring match, but not a total word match so return non-zero
     } else if !big_len_diff {
         // Not a big difference in length, discount cost of length difference
-        score + (len_diff + 1) / 2
+        score + len_diff.div_ceil(2)
     } else {
         // A big difference in length, add back the difference in length to the score
         score + len_diff
diff --git a/compiler-core/src/exhaustiveness.rs b/compiler-core/src/exhaustiveness.rs
index e7c63677e..93d601f4f 100644
--- a/compiler-core/src/exhaustiveness.rs
+++ b/compiler-core/src/exhaustiveness.rs
@@ -235,7 +235,6 @@ impl Branch {
                     Pattern::Assign { name, pattern } => {
                         self.body.assign(name.clone(), check.var.clone());
                         check.pattern = *pattern;
-                        continue;
                     }
                     // There's a special case of assignments when it comes to string
                     // prefix patterns. We can give a name to a literal prefix like this:
diff --git a/compiler-core/src/exhaustiveness/printer.rs b/compiler-core/src/exhaustiveness/printer.rs
index 07b952e02..13631023c 100644
--- a/compiler-core/src/exhaustiveness/printer.rs
+++ b/compiler-core/src/exhaustiveness/printer.rs
@@ -57,7 +57,7 @@ impl<'a> Printer<'a> {
                     NameContextInformation::Qualified(m, n) => (Some(m), n),
                     NameContextInformation::Unqualified(n) => (None, n),
                     NameContextInformation::Unimported(n) => {
-                        (Some(module.split('/').last().unwrap_or(module)), n)
+                        (Some(module.split('/').next_back().unwrap_or(module)), n)
                     }
                 };
 
diff --git a/compiler-core/src/format.rs b/compiler-core/src/format.rs
index f11b08c33..6a79db474 100644
--- a/compiler-core/src/format.rs
+++ b/compiler-core/src/format.rs
@@ -412,7 +412,7 @@ impl<'comments> Formatter<'comments> {
                 };
 
                 let doc = docvec!["import ", module.as_str(), second];
-                let default_module_access_name = module.split('/').last().map(EcoString::from);
+                let default_module_access_name = module.split('/').next_back().map(EcoString::from);
                 match (default_module_access_name, as_name) {
                     // If the `as name` is the same as the module name that would be
                     // used anyways we won't render it. For example:
diff --git a/compiler-core/src/javascript.rs b/compiler-core/src/javascript.rs
index 26488fd22..d41e3aa9c 100644
--- a/compiler-core/src/javascript.rs
+++ b/compiler-core/src/javascript.rs
@@ -90,7 +90,7 @@ impl<'a> Generator<'a> {
             .name
             .as_str()
             .split('/')
-            .last()
+            .next_back()
             .expect("JavaScript generator could not identify imported module name.");
 
         docvec!["/// <reference types=\"./", module, ".d.mts\" />", line()]
@@ -453,7 +453,7 @@ impl<'a> Generator<'a> {
         let get_name = |module: &'a str| {
             module
                 .split('/')
-                .last()
+                .next_back()
                 .expect("JavaScript generator could not identify imported module name.")
         };
 
diff --git a/compiler-core/src/javascript/typescript.rs b/compiler-core/src/javascript/typescript.rs
index b8a0230b8..76eb3d6f4 100644
--- a/compiler-core/src/javascript/typescript.rs
+++ b/compiler-core/src/javascript/typescript.rs
@@ -626,7 +626,7 @@ impl<'a> TypeScriptGenerator<'a> {
 
         let name = match self.aliased_module_names.get(name) {
             Some(name) => name,
-            None => name.split('/').last().expect("Non empty module path"),
+            None => name.split('/').next_back().expect("Non empty module path"),
         };
 
         eco_format!("${name}")
diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs
index 9b3df744c..03149ee2a 100644
--- a/compiler-core/src/language_server/code_action.rs
+++ b/compiler-core/src/language_server/code_action.rs
@@ -275,7 +275,7 @@ impl<'a> RedundantTupleInCaseSubject<'a> {
         // tuple items.
         self.edits.replace(
             discard_location,
-            itertools::intersperse(iter::repeat("_").take(tuple_items), ", ").collect(),
+            itertools::intersperse(iter::repeat_n("_", tuple_items), ", ").collect(),
         )
     }
 }
@@ -724,7 +724,7 @@ impl<'a> FillInMissingLabelledArgs<'a> {
             //
             let label_insertion_start = call_location.end - 1;
             let has_comma_after_last_argument =
-                if let Some(last_arg) = args.iter().filter(|arg| !arg.is_implicit()).last() {
+                if let Some(last_arg) = args.iter().filter(|arg| !arg.is_implicit()).next_back() {
                     self.module
                         .code
                         .get(last_arg.location.end as usize..=label_insertion_start as usize)
@@ -2310,7 +2310,7 @@ impl<'a> ConvertFromUse<'a> {
             .get(use_line_end as usize - 1..use_line_end as usize)
             == Some(")");
 
-        let last_explicit_arg = args.iter().filter(|arg| !arg.is_implicit()).last();
+        let last_explicit_arg = args.iter().filter(|arg| !arg.is_implicit()).next_back();
         let last_arg_end = last_explicit_arg.map_or(use_line_end - 1, |arg| arg.location.end);
 
         // This is the piece of code between the end of the last argument and
@@ -3023,8 +3023,8 @@ fn can_be_constant(
         }
 
         // Extract record types as long as arguments can be constant
-        TypedExpr::Call { args, type_, .. } => {
-            matches!(type_.as_ref(), Type::Named { .. })
+        TypedExpr::Call { args, fun, .. } => {
+            fun.is_record_builder()
                 && args
                     .iter()
                     .all(|arg| can_be_constant(module, &arg.value, module_constants))
@@ -4868,7 +4868,7 @@ fn labels_are_correct(args: &[TypedCallArg]) -> bool {
                 labelled_arg_found = true;
                 let _ = used_labels.insert(label);
             }
-            None => continue,
+            None => {}
         }
     }
 
@@ -5849,7 +5849,7 @@ impl<'ast> ast::visit::Visit<'ast> for FillUnusedFields<'ast> {
                 let last_argument_end = arguments
                     .iter()
                     .filter(|arg| !arg.is_implicit())
-                    .last()
+                    .next_back()
                     .map(|arg| arg.location.end);
 
                 self.data = Some(FillUnusedFieldsData {
@@ -6100,15 +6100,8 @@ impl<'ast> ast::visit::Visit<'ast> for WrapInBlock<'ast> {
         match *assignment.to_owned().value {
             // To avoid wrapping the same expression in multiple, nested blocks.
             TypedExpr::Block { .. } => {}
-            TypedExpr::RecordAccess {
-                record, location, ..
-            } => {
-                self.selected_expression = Some(SrcSpan {
-                    start: record.location().start,
-                    end: location.end,
-                });
-            }
-            TypedExpr::Int { .. }
+            TypedExpr::RecordAccess { .. }
+            | TypedExpr::Int { .. }
             | TypedExpr::Float { .. }
             | TypedExpr::String { .. }
             | TypedExpr::Pipeline { .. }
diff --git a/compiler-core/src/language_server/completer.rs b/compiler-core/src/language_server/completer.rs
index 4b606411e..9b194c7ad 100644
--- a/compiler-core/src/language_server/completer.rs
+++ b/compiler-core/src/language_server/completer.rs
@@ -469,16 +469,15 @@ where
             // e.x. when the user has typed mymodule.| we know unqualified module types are no longer relevant.
             if module_select.is_none() {
                 for unqualified in &import.unqualified_types {
-                    match module.get_public_type(&unqualified.name) {
-                        Some(type_) => completions.push(type_completion(
+                    if let Some(type_) = module.get_public_type(&unqualified.name) {
+                        completions.push(type_completion(
                             None,
                             unqualified.used_name(),
                             type_,
                             insert_range,
                             TypeCompletionForm::Default,
                             CompletionKind::ImportedModule,
-                        )),
-                        None => continue,
+                        ))
                     }
                 }
             }
@@ -504,7 +503,7 @@ where
 
             let qualifier = module_full_name
                 .split('/')
-                .last()
+                .next_back()
                 .unwrap_or(module_full_name);
 
             // If the user has already started a module select then don't show irrelevant modules.
@@ -657,19 +656,16 @@ where
             // e.x. when the user has typed mymodule.| we know unqualified module values are no longer relevant.
             if module_select.is_none() {
                 for unqualified in &import.unqualified_values {
-                    match module.get_public_value(&unqualified.name) {
-                        Some(value) => {
-                            let name = unqualified.used_name();
-                            completions.push(value_completion(
-                                None,
-                                mod_name,
-                                name,
-                                value,
-                                insert_range,
-                                CompletionKind::ImportedModule,
-                            ))
-                        }
-                        None => continue,
+                    if let Some(value) = module.get_public_value(&unqualified.name) {
+                        let name = unqualified.used_name();
+                        completions.push(value_completion(
+                            None,
+                            mod_name,
+                            name,
+                            value,
+                            insert_range,
+                            CompletionKind::ImportedModule,
+                        ))
                     }
                 }
             }
@@ -693,7 +689,7 @@ where
             }
             let qualifier = module_full_name
                 .split('/')
-                .last()
+                .next_back()
                 .unwrap_or(module_full_name);
 
             // If the user has already started a module select then don't show irrelevant modules.
diff --git a/compiler-core/src/language_server/messages.rs b/compiler-core/src/language_server/messages.rs
index eef0849ef..2d2b7318c 100644
--- a/compiler-core/src/language_server/messages.rs
+++ b/compiler-core/src/language_server/messages.rs
@@ -114,7 +114,7 @@ impl Notification {
                 let params = cast_notification::<DidChangeTextDocument>(notification);
                 let notification = Notification::SourceFileChangedInMemory {
                     path: super::path(&params.text_document.uri),
-                    text: params.content_changes.into_iter().last()?.text,
+                    text: params.content_changes.into_iter().next_back()?.text,
                 };
                 Some(Message::Notification(notification))
             }
@@ -137,7 +137,7 @@ impl Notification {
             "workspace/didChangeWatchedFiles" => {
                 let params = cast_notification::<DidChangeWatchedFiles>(notification);
                 let notification = Notification::ConfigFileChanged {
-                    path: super::path(&params.changes.into_iter().last()?.uri),
+                    path: super::path(&params.changes.into_iter().next_back()?.uri),
                 };
                 Some(Message::Notification(notification))
             }
diff --git a/compiler-core/src/parse.rs b/compiler-core/src/parse.rs
index 7b53f922b..9c59b1d77 100644
--- a/compiler-core/src/parse.rs
+++ b/compiler-core/src/parse.rs
@@ -3388,8 +3388,7 @@ where
                         Token::Name { name } => name,
                         token => {
                             let hint = match (&token, self.tok0.take()) {
-                                (&Token::Fn { .. }, _)
-                                | (&Token::Pub, Some((_, Token::Fn { .. }, _))) => {
+                                (&Token::Fn, _) | (&Token::Pub, Some((_, Token::Fn, _))) => {
                                     let text =
                                         "Gleam is not an object oriented programming language so
 functions are declared separately from types.";
diff --git a/compiler-core/src/type_/environment.rs b/compiler-core/src/type_/environment.rs
index 3fda1ad6f..c68a1727f 100644
--- a/compiler-core/src/type_/environment.rs
+++ b/compiler-core/src/type_/environment.rs
@@ -702,7 +702,7 @@ impl Environment<'_> {
                     // Don't suggest importing modules if they are already imported
                     _ if self
                         .imported_modules
-                        .contains_key(importable.split('/').last().unwrap_or(importable)) =>
+                        .contains_key(importable.split('/').next_back().unwrap_or(importable)) =>
                     {
                         None
                     }
diff --git a/compiler-core/src/type_/error.rs b/compiler-core/src/type_/error.rs
index e0ea90c58..6f4bee455 100644
--- a/compiler-core/src/type_/error.rs
+++ b/compiler-core/src/type_/error.rs
@@ -137,7 +137,7 @@ impl ModuleSuggestion {
     pub fn last_name_component(&self) -> &str {
         match self {
             ModuleSuggestion::Imported(name) | ModuleSuggestion::Importable(name) => {
-                name.split('/').last().unwrap_or(name)
+                name.split('/').next_back().unwrap_or(name)
             }
         }
     }
diff --git a/compiler-core/src/type_/expression.rs b/compiler-core/src/type_/expression.rs
index 09c8abd73..1a0d166a6 100644
--- a/compiler-core/src/type_/expression.rs
+++ b/compiler-core/src/type_/expression.rs
@@ -1032,12 +1032,18 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
         label_location: SrcSpan,
         usage: FieldAccessUsage,
     ) -> TypedExpr {
+        let container_location = container.location();
+
         // Computes a potential module access. This will be used if a record access can't be used.
         // Computes both the inferred access and if it shadows a variable.
         let module_access = match &container {
-            UntypedExpr::Var { location, name } => {
-                let module_access =
-                    self.infer_module_access(name, label.clone(), location, label_location);
+            UntypedExpr::Var { name, .. } => {
+                let module_access = self.infer_module_access(
+                    name,
+                    label.clone(),
+                    &container_location,
+                    label_location,
+                );
                 // Returns the result and if it shadows an existing variable in scope
                 Some((module_access, self.environment.scope.contains_key(name)))
             }
@@ -1060,7 +1066,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
             Ok(record) => self.infer_known_record_expression_access(
                 record,
                 label.clone(),
+                location,
                 label_location,
+                container_location.start,
                 usage,
             ),
             Err(e) => Err(e),
@@ -1139,7 +1147,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
                     // This allows autocomplete to know a record access is being attempted
                     // Even if the access is not valid
                     Ok(record) => TypedExpr::RecordAccess {
-                        location: label_location,
+                        location,
+                        field_start: container_location.start,
                         type_: self.new_unbound_var(),
                         label: "".into(),
                         index: u64::MAX,
@@ -2528,15 +2537,23 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
         record: TypedExpr,
         label: EcoString,
         location: SrcSpan,
+        label_location: SrcSpan,
+        field_start: u32,
         usage: FieldAccessUsage,
     ) -> Result<TypedExpr, Error> {
         let record = Box::new(record);
         let record_type = record.type_();
-        let (index, label, type_) =
-            self.infer_known_record_access(record_type, record.location(), usage, location, label)?;
+        let (index, label, type_) = self.infer_known_record_access(
+            record_type,
+            record.location(),
+            usage,
+            label_location,
+            label,
+        )?;
         Ok(TypedExpr::RecordAccess {
             record,
             label,
+            field_start,
             index,
             location,
             type_,
@@ -2810,6 +2827,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
                     record.clone(),
                     label.clone(),
                     record_location,
+                    record_location,
+                    record_location.start,
                     FieldAccessUsage::RecordUpdate,
                 )?;
 
diff --git a/compiler-core/src/type_/printer.rs b/compiler-core/src/type_/printer.rs
index 425a93976..9464ef489 100644
--- a/compiler-core/src/type_/printer.rs
+++ b/compiler-core/src/type_/printer.rs
@@ -344,7 +344,7 @@ impl<'a> Printer<'a> {
                     // TODO: indicate that the module is not import and as such
                     // needs to be, as well as how.
                     NameContextInformation::Unimported(n) => {
-                        (Some(module.split('/').last().unwrap_or(module)), n)
+                        (Some(module.split('/').next_back().unwrap_or(module)), n)
                     }
                 };
 
@@ -388,7 +388,7 @@ impl<'a> Printer<'a> {
             NameContextInformation::Qualified(module, name) => (Some(module), name),
             NameContextInformation::Unqualified(name) => (None, name),
             NameContextInformation::Unimported(name) => {
-                (Some(module.split('/').last().unwrap_or(module)), name)
+                (Some(module.split('/').next_back().unwrap_or(module)), name)
             }
         };
 


Test patch:
diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs
index 48a3f7b0e..072db1e74 100644
--- a/compiler-core/src/language_server/tests/action.rs
+++ b/compiler-core/src/language_server/tests/action.rs
@@ -7179,6 +7179,26 @@ pub fn main() {
     );
 }
 
+// https://github.com/gleam-lang/gleam/issues/4430
+#[test]
+fn inline_variable_with_record_field() {
+    assert_code_action!(
+        INLINE_VARIABLE,
+        "
+type Couple {
+  Couple(l: Int, r: Int)
+}
+
+pub fn main() {
+  let c1 = Couple(l: 1, r: 1)
+  let c2 = c1.l
+  echo c2
+}
+",
+        find_position_of("c2").nth_occurrence(2).to_selection()
+    );
+}
+
 #[test]
 fn wrap_case_clause_in_block() {
     assert_code_action!(
@@ -7239,7 +7259,7 @@ fn wrap_case_clause_with_multiple_patterns_in_block() {
   Water
   Fire
 }
-        
+
   pub fn f(pokemon_type: PokemonType) {
     case pokemon_type {
       Water | Air -> soak()
@@ -7259,7 +7279,7 @@ fn wrap_case_clause_inside_assignment_in_block() {
   Water
   Fire
 }
-        
+
   pub fn f(pokemon_type: PokemonType) {
     let damage = case pokemon_type {
       Water -> soak()
@@ -7366,3 +7386,21 @@ fn do_not_wrap_assignment_value_in_block() {
         find_position_of("var").to_selection()
     );
 }
+
+// https://github.com/gleam-lang/gleam/issues/4427
+#[test]
+fn extract_constant_function() {
+    assert_no_code_actions!(
+        EXTRACT_CONSTANT,
+        r#"
+fn print(x) {
+  Nil
+}
+
+pub fn main() {
+  print("Hello")
+}
+"#,
+        find_position_of("print").nth_occurrence(2).to_selection()
+    );
+}
diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__inline_variable_with_record_field.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__inline_variable_with_record_field.snap
new file mode 100644
index 000000000..d59e3498d
--- /dev/null
+++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__inline_variable_with_record_field.snap
@@ -0,0 +1,28 @@
+---
+source: compiler-core/src/language_server/tests/action.rs
+expression: "\ntype Couple {\n  Couple(l: Int, r: Int)\n}\n\npub fn main() {\n  let c1 = Couple(l: 1, r: 1)\n  let c2 = c1.l\n  echo c2\n}\n"
+---
+----- BEFORE ACTION
+
+type Couple {
+  Couple(l: Int, r: Int)
+}
+
+pub fn main() {
+  let c1 = Couple(l: 1, r: 1)
+  let c2 = c1.l
+  echo c2
+       ↑ 
+}
+
+
+----- AFTER ACTION
+
+type Couple {
+  Couple(l: Int, r: Int)
+}
+
+pub fn main() {
+  let c1 = Couple(l: 1, r: 1)
+  echo c1.l
+}
diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__wrap_case_clause_inside_assignment_in_block.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__wrap_case_clause_inside_assignment_in_block.snap
index 0ad56dbb5..16e735ef9 100644
--- a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__wrap_case_clause_inside_assignment_in_block.snap
+++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__wrap_case_clause_inside_assignment_in_block.snap
@@ -1,6 +1,6 @@
 ---
 source: compiler-core/src/language_server/tests/action.rs
-expression: "pub type PokemonType {\n  Air\n  Water\n  Fire\n}\n        \n  pub fn f(pokemon_type: PokemonType) {\n    let damage = case pokemon_type {\n      Water -> soak()\n      Fire -> burn()\n    }\n\n    \"Pokemon did \" <> damage\n  }"
+expression: "pub type PokemonType {\n  Air\n  Water\n  Fire\n}\n\n  pub fn f(pokemon_type: PokemonType) {\n    let damage = case pokemon_type {\n      Water -> soak()\n      Fire -> burn()\n    }\n\n    \"Pokemon did \" <> damage\n  }"
 ---
 ----- BEFORE ACTION
 pub type PokemonType {
@@ -8,7 +8,7 @@ pub type PokemonType {
   Water
   Fire
 }
-        
+
   pub fn f(pokemon_type: PokemonType) {
     let damage = case pokemon_type {
       Water -> soak()
@@ -26,7 +26,7 @@ pub type PokemonType {
   Water
   Fire
 }
-        
+
   pub fn f(pokemon_type: PokemonType) {
     let damage = case pokemon_type {
       Water -> soak()
diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__wrap_case_clause_with_multiple_patterns_in_block.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__wrap_case_clause_with_multiple_patterns_in_block.snap
index 815f3fbb8..28574783f 100644
--- a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__wrap_case_clause_with_multiple_patterns_in_block.snap
+++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__wrap_case_clause_with_multiple_patterns_in_block.snap
@@ -1,6 +1,6 @@
 ---
 source: compiler-core/src/language_server/tests/action.rs
-expression: "pub type PokemonType {\n  Air\n  Water\n  Fire\n}\n        \n  pub fn f(pokemon_type: PokemonType) {\n    case pokemon_type {\n      Water | Air -> soak()\n      Fire -> burn()\n    }\n  }"
+expression: "pub type PokemonType {\n  Air\n  Water\n  Fire\n}\n\n  pub fn f(pokemon_type: PokemonType) {\n    case pokemon_type {\n      Water | Air -> soak()\n      Fire -> burn()\n    }\n  }"
 ---
 ----- BEFORE ACTION
 pub type PokemonType {
@@ -8,7 +8,7 @@ pub type PokemonType {
   Water
   Fire
 }
-        
+
   pub fn f(pokemon_type: PokemonType) {
     case pokemon_type {
       Water | Air -> soak()
@@ -24,7 +24,7 @@ pub type PokemonType {
   Water
   Fire
 }
-        
+
   pub fn f(pokemon_type: PokemonType) {
     case pokemon_type {
       Water | Air -> {
diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__unused_record_access_raises_a_warning.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__unused_record_access_raises_a_warning.snap
index d6cbbc0d3..57697d291 100644
--- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__unused_record_access_raises_a_warning.snap
+++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__unused_record_access_raises_a_warning.snap
@@ -17,7 +17,7 @@ pub fn main() {
 
 ----- WARNING
 warning: Unused value
-  ┌─ /src/warning/wrn.gleam:8:9
+  ┌─ /src/warning/wrn.gleam:8:3
   │
 8 │   thing.value
-  │         ^^^^^ This value is never used
+  │   ^^^^^^^^^^^ This value is never used
diff --git a/test/running_modules/gleam.toml b/test/running_modules/gleam.toml
index 1ca816ca4..11709fab5 100644
--- a/test/running_modules/gleam.toml
+++ b/test/running_modules/gleam.toml
@@ -3,11 +3,8 @@ version = "0.1.0"
 description = "A Gleam project"
 
 [dependencies]
-gleam_stdlib = "~> 0.27"
-gleeunit = "~> 0.10"
-# Project used to test if javascript target is detected.
-# https://hex.pm/packages/gleam_module_javascript_test
-gleam_module_javascript_test = "~> 0.2.0"
+gleam_stdlib = ">= 0.58.0 and < 2.0.0"
+gleeunit = ">= 1.0.0 and < 2.0.0"
 
 [javascript.deno]
 allow_read = true
diff --git a/test/running_modules/manifest.toml b/test/running_modules/manifest.toml
index b5ea31984..dca89c33a 100644
--- a/test/running_modules/manifest.toml
+++ b/test/running_modules/manifest.toml
@@ -2,12 +2,10 @@
 # You typically do not need to edit this file
 
 packages = [
-  { name = "gleam_module_javascript_test", version = "0.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_module_javascript_test", source = "hex", outer_checksum = "EB67B6423078E8A37FCB172EC5342BAA1CF45A235F3A9C890532E7487E606E91" },
-  { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" },
-  { name = "gleeunit", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "1397E5C4AC4108769EE979939AC39BF7870659C5AFB714630DEEEE16B8272AD5" },
+  { name = "gleam_stdlib", version = "0.58.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "091F2D2C4A3A4E2047986C47E2C2C9D728A4E068ABB31FDA17B0D347E6248467" },
+  { name = "gleeunit", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "0E6C83834BA65EDCAAF4FE4FB94AC697D9262D83E6F58A750D63C9F6C8A9D9FF" },
 ]
 
 [requirements]
-gleam_module_javascript_test = { version = "~> 0.2.0" }
-gleam_stdlib = { version = "~> 0.27" }
-gleeunit = { version = "~> 0.10" }
+gleam_stdlib = { version = ">= 0.58.0 and < 2.0.0" }
+gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
diff --git a/test/running_modules/run_tests.sh b/test/running_modules/run_tests.sh
index 8fc82dd03..3e3bab300 100755
--- a/test/running_modules/run_tests.sh
+++ b/test/running_modules/run_tests.sh
@@ -61,10 +61,3 @@ should_fail run --module module/no_main_function
 
 # Main function with wrong arity
 should_fail run --module module/wrong_arity
-
-# try running gleam_module_javascript_test, which will crash if target is erlang
-should_succeed run --module gleam_module_javascript_test
-
-# verify that javascript target doesn't crash and erlang target does crash
-should_succeed run --module gleam_module_javascript_test --target=javascript
-should_fail run --module gleam_module_javascript_test --target=erlang