From 0eaa3366f904e9fde3bd3ada9d2642f23140577d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 11 Oct 2025 12:04:17 +0900 Subject: [PATCH 01/12] Improve error message for ambiguous numeric types in closure parameters --- compiler/rustc_hir_typeck/src/method/suggest.rs | 16 ++++++++++++++++ .../ambiguous-numeric-in-closure-ref.fixed | 9 +++++++++ .../ambiguous-numeric-in-closure-ref.rs | 9 +++++++++ .../ambiguous-numeric-in-closure-ref.stderr | 16 ++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed create mode 100644 tests/ui/inference/ambiguous-numeric-in-closure-ref.rs create mode 100644 tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 44602e6289940..3c3c979c05ec5 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2568,6 +2568,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } + // For closure parameters with reference patterns (e.g., |&v|), suggest the type annotation + // on the pattern itself, e.g., |&v: &i32| + (FileName::Real(_), Node::Pat(pat)) + if let Node::Pat(binding_pat) = self.tcx.hir_node(hir_id) + && let hir::PatKind::Binding(..) = binding_pat.kind + && let Node::Pat(parent_pat) = parent_node + && matches!(parent_pat.kind, hir::PatKind::Ref(..)) => + { + err.span_label(span, "you must specify a type for this binding"); + err.span_suggestion_verbose( + pat.span.shrink_to_hi(), + "specify the type in the closure argument list", + format!(": &{concrete_type}"), + Applicability::MaybeIncorrect, + ); + } _ => { err.span_label(span, msg); } diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed b/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed new file mode 100644 index 0000000000000..0bb81c7df125d --- /dev/null +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed @@ -0,0 +1,9 @@ +// Test for better error message when numeric type is ambiguous in closure parameter with reference + +//@ run-rustfix + +fn main() { + let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + //~| SUGGESTION &i32 +} diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs b/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs new file mode 100644 index 0000000000000..7a6f779ab8329 --- /dev/null +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs @@ -0,0 +1,9 @@ +// Test for better error message when numeric type is ambiguous in closure parameter with reference + +//@ run-rustfix + +fn main() { + let _ = (0..10).filter(|&v| v.pow(2) > 0); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + //~| SUGGESTION &i32 +} diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr b/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr new file mode 100644 index 0000000000000..a2225d940a30f --- /dev/null +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr @@ -0,0 +1,16 @@ +error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` + --> $DIR/ambiguous-numeric-in-closure-ref.rs:6:35 + | +LL | let _ = (0..10).filter(|&v| v.pow(2) > 0); + | - ^^^ + | | + | you must specify a type for this binding + | +help: specify the type in the closure argument list + | +LL | let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0); + | ++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0689`. From dbc7327748d6dc190c594c8343dd53e8e60ca49c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 11 Oct 2025 21:17:17 +0900 Subject: [PATCH 02/12] Fix suggestions for nested refs and muts --- .../rustc_hir_typeck/src/method/suggest.rs | 24 ++++++++++++++++++- .../ambiguous-numeric-in-closure-ref.fixed | 5 ++++ .../ambiguous-numeric-in-closure-ref.rs | 5 ++++ .../ambiguous-numeric-in-closure-ref.stderr | 15 +++++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 3c3c979c05ec5..a2ec7075fac94 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2577,10 +2577,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && matches!(parent_pat.kind, hir::PatKind::Ref(..)) => { err.span_label(span, "you must specify a type for this binding"); + + let mut ref_muts = Vec::new(); + let mut current_node = parent_node; + + while let Node::Pat(parent_pat) = current_node { + if let hir::PatKind::Ref(_, mutability) = parent_pat.kind { + ref_muts.push(mutability); + current_node = self.tcx.parent_hir_node(parent_pat.hir_id); + } else { + break; + } + } + + let mut type_annotation = String::new(); + for mutability in ref_muts.iter().rev() { + match mutability { + hir::Mutability::Mut => type_annotation.push_str("&mut "), + hir::Mutability::Not => type_annotation.push('&'), + } + } + type_annotation.push_str(&concrete_type); + err.span_suggestion_verbose( pat.span.shrink_to_hi(), "specify the type in the closure argument list", - format!(": &{concrete_type}"), + format!(": {type_annotation}"), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed b/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed index 0bb81c7df125d..683d581d034b1 100644 --- a/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.fixed @@ -6,4 +6,9 @@ fn main() { let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0); //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` //~| SUGGESTION &i32 + + let v = vec![0, 1, 2]; + let _ = v.iter().filter(|&&v: &&i32| v.pow(2) > 0); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + //~| SUGGESTION &&i32 } diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs b/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs index 7a6f779ab8329..eab309c701d69 100644 --- a/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.rs @@ -6,4 +6,9 @@ fn main() { let _ = (0..10).filter(|&v| v.pow(2) > 0); //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` //~| SUGGESTION &i32 + + let v = vec![0, 1, 2]; + let _ = v.iter().filter(|&&v| v.pow(2) > 0); + //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}` + //~| SUGGESTION &&i32 } diff --git a/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr b/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr index a2225d940a30f..0789581abea80 100644 --- a/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr +++ b/tests/ui/inference/ambiguous-numeric-in-closure-ref.stderr @@ -11,6 +11,19 @@ help: specify the type in the closure argument list LL | let _ = (0..10).filter(|&v: &i32| v.pow(2) > 0); | ++++++ -error: aborting due to 1 previous error +error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` + --> $DIR/ambiguous-numeric-in-closure-ref.rs:11:37 + | +LL | let _ = v.iter().filter(|&&v| v.pow(2) > 0); + | - ^^^ + | | + | you must specify a type for this binding + | +help: specify the type in the closure argument list + | +LL | let _ = v.iter().filter(|&&v: &&i32| v.pow(2) > 0); + | +++++++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0689`. From 632e759497ec9ec93b96c863479c8512f9511650 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 11 Oct 2025 21:32:47 +0900 Subject: [PATCH 03/12] Add FIXME about FileName check's fragility --- compiler/rustc_hir_typeck/src/method/suggest.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a2ec7075fac94..e1073de49d305 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2547,6 +2547,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "you must specify a type for this binding, like `{concrete_type}`", ); + // FIXME: Maybe FileName::Anon should also be handled, + // otherwise there would be no suggestion if the source is STDIN for example. match (filename, parent_node) { ( FileName::Real(_), From 7c95768dbd8a9e376c3d8724a28f327d556686e1 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 17 Oct 2025 15:22:54 +0000 Subject: [PATCH 04/12] btree: some cleanup with less unsafe --- library/alloc/src/collections/btree/node.rs | 33 +++++++++------------ 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index a87259e7c58f2..84dd4b7e49def 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -33,6 +33,7 @@ use core::marker::PhantomData; use core::mem::{self, MaybeUninit}; +use core::num::NonZero; use core::ptr::{self, NonNull}; use core::slice::SliceIndex; @@ -143,7 +144,7 @@ type BoxedNode = NonNull>; /// /// A reference to a node. /// -/// This type has a number of parameters that controls how it acts: +/// This type has a number of parameters that control how it acts: /// - `BorrowType`: A dummy type that describes the kind of borrow and carries a lifetime. /// - When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`. /// - When this is `ValMut<'a>`, the `NodeRef` acts roughly like `&'a Node` @@ -226,33 +227,27 @@ impl NodeRef { fn from_new_leaf(leaf: Box, A>) -> Self { // The allocator must be dropped, not leaked. See also `BTreeMap::alloc`. - let (leaf, _alloc) = Box::into_raw_with_allocator(leaf); - // SAFETY: the node was just allocated. - let node = unsafe { NonNull::new_unchecked(leaf) }; + let (node, _alloc) = Box::into_non_null_with_allocator(leaf); NodeRef { height: 0, node, _marker: PhantomData } } } impl NodeRef { + /// Creates a new internal (height > 0) `NodeRef` fn new_internal(child: Root, alloc: A) -> Self { let mut new_node = unsafe { InternalNode::new(alloc) }; new_node.edges[0].write(child.node); - unsafe { NodeRef::from_new_internal(new_node, child.height + 1) } + NodeRef::from_new_internal(new_node, NonZero::new(child.height + 1).unwrap()) } - /// # Safety - /// `height` must not be zero. - unsafe fn from_new_internal( + /// Creates a new internal (height > 0) `NodeRef` from an existing internal node + fn from_new_internal( internal: Box, A>, - height: usize, + height: NonZero, ) -> Self { - debug_assert!(height > 0); // The allocator must be dropped, not leaked. See also `BTreeMap::alloc`. - let (internal, _alloc) = Box::into_raw_with_allocator(internal); - // SAFETY: the node was just allocated. - let internal = unsafe { NonNull::new_unchecked(internal) }; - let node = internal.cast(); - let mut this = NodeRef { height, node, _marker: PhantomData }; + let (node, _alloc) = Box::into_non_null_with_allocator(internal); + let mut this = NodeRef { height: height.into(), node: node.cast(), _marker: PhantomData }; this.borrow_mut().correct_all_childrens_parent_links(); this } @@ -625,9 +620,8 @@ impl NodeRef { let top = self.node; // SAFETY: we asserted to be internal. - let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() }; - // SAFETY: we borrowed `self` exclusively and its borrow type is exclusive. - let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) }; + let mut internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() }; + let internal_node = internal_self.as_internal_mut(); // SAFETY: the first edge is always initialized. self.node = unsafe { internal_node.edges[0].assume_init_read() }; self.height -= 1; @@ -1305,7 +1299,8 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, &mut new_node.edges[..new_len + 1], ); - let height = self.node.height; + // SAFETY: self is `marker::Internal`, so `self.node.height` is positive + let height = NonZero::new_unchecked(self.node.height); let right = NodeRef::from_new_internal(new_node, height); SplitResult { left: self.node, kv, right } From 66c81a0024da4503deb4a782d1ae6e375e79f4a7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:49:20 +0000 Subject: [PATCH 05/12] tidy: Add check that license exceptions are actually used --- src/tools/tidy/src/deps.rs | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 3164dcc77be1d..66d035cd01156 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -34,7 +34,7 @@ const LICENSES: &[&str] = &[ "MIT / Apache-2.0", "MIT AND (MIT OR Apache-2.0)", "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)", // compiler-builtins - "MIT OR Apache-2.0 OR LGPL-2.1-or-later", // r-efi, r-efi-alloc + "MIT OR Apache-2.0 OR LGPL-2.1-or-later", // r-efi, r-efi-alloc; LGPL is not acceptable, but we use it under MIT OR Apache-2.0 "MIT OR Apache-2.0 OR Zlib", // tinyvec_macros "MIT OR Apache-2.0", "MIT OR Zlib OR Apache-2.0", // miniz_oxide @@ -174,13 +174,10 @@ const EXCEPTIONS: ExceptionList = &[ ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), // rustc ("colored", "MPL-2.0"), // rustfmt ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), // rustc - ("dissimilar", "Apache-2.0"), // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps) - ("fluent-langneg", "Apache-2.0"), // rustc (fluent translations) ("foldhash", "Zlib"), // rustc ("option-ext", "MPL-2.0"), // cargo-miri (via `directories`) ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses) ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde) - ("self_cell", "Apache-2.0"), // rustc (fluent translations) ("wasi-preview1-component-adapter-provider", "Apache-2.0 WITH LLVM-exception"), // rustc // tidy-alphabetical-end ]; @@ -201,9 +198,6 @@ const EXCEPTIONS_CARGO: ExceptionList = &[ ("arrayref", "BSD-2-Clause"), ("bitmaps", "MPL-2.0+"), ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), - ("ciborium", "Apache-2.0"), - ("ciborium-io", "Apache-2.0"), - ("ciborium-ll", "Apache-2.0"), ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"), ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), @@ -211,30 +205,22 @@ const EXCEPTIONS_CARGO: ExceptionList = &[ ("foldhash", "Zlib"), ("im-rc", "MPL-2.0+"), ("libz-rs-sys", "Zlib"), - ("normalize-line-endings", "Apache-2.0"), - ("openssl", "Apache-2.0"), ("ring", "Apache-2.0 AND ISC"), ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 - ("similar", "Apache-2.0"), ("sized-chunks", "MPL-2.0+"), ("subtle", "BSD-3-Clause"), - ("supports-hyperlinks", "Apache-2.0"), - ("unicode-bom", "Apache-2.0"), ("zlib-rs", "Zlib"), // tidy-alphabetical-end ]; const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ // tidy-alphabetical-start - ("dissimilar", "Apache-2.0"), ("foldhash", "Zlib"), ("notify", "CC0-1.0"), ("option-ext", "MPL-2.0"), - ("pulldown-cmark-to-cmark", "Apache-2.0"), ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 - ("scip", "Apache-2.0"), - // tidy-alphabetical-end + // tidy-alphabetical-end ]; const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[ @@ -300,9 +286,7 @@ const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[ ("ryu", "Apache-2.0 OR BSL-1.0"), // through serde. BSL is not acceptble, but we use it under Apache-2.0 ]; -const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[ - ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptable, but we use it under MIT OR Apache-2.0 -]; +const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[]; #[derive(Clone, Copy)] struct ListLocation { @@ -867,6 +851,11 @@ fn check_license_exceptions( } } } + if LICENSES.contains(license) { + check.error(format!( + "dependency exception `{name}` is not necessary. `{license}` is an allowed license" + )); + } } let exception_names: Vec<_> = exceptions.iter().map(|(name, _license)| *name).collect(); From 3621785401cfce280ceff7216f680d2814194c2e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:51:46 +0000 Subject: [PATCH 06/12] Add Apache-2.0 WITH LLVM-exception to list of allowed licenses It is more permissive than Apache-2.0 which is already allowed. --- src/tools/tidy/src/deps.rs | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 66d035cd01156..dd885e1e9d88b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -27,6 +27,7 @@ const LICENSES: &[&str] = &[ "Apache-2.0 OR ISC OR MIT", "Apache-2.0 OR MIT", "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license + "Apache-2.0 WITH LLVM-exception", "Apache-2.0", "Apache-2.0/MIT", "BSD-2-Clause OR Apache-2.0 OR MIT", // zerocopy @@ -169,16 +170,13 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ #[rustfmt::skip] const EXCEPTIONS: ExceptionList = &[ // tidy-alphabetical-start - ("ar_archive_writer", "Apache-2.0 WITH LLVM-exception"), // rustc ("arrayref", "BSD-2-Clause"), // rustc ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), // rustc ("colored", "MPL-2.0"), // rustfmt ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), // rustc ("foldhash", "Zlib"), // rustc ("option-ext", "MPL-2.0"), // cargo-miri (via `directories`) - ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses) ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde) - ("wasi-preview1-component-adapter-provider", "Apache-2.0 WITH LLVM-exception"), // rustc // tidy-alphabetical-end ]; @@ -218,7 +216,6 @@ const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ ("foldhash", "Zlib"), ("notify", "CC0-1.0"), ("option-ext", "MPL-2.0"), - ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // tidy-alphabetical-end ]; @@ -250,28 +247,8 @@ const EXCEPTIONS_RUSTBOOK: ExceptionList = &[ const EXCEPTIONS_CRANELIFT: ExceptionList = &[ // tidy-alphabetical-start - ("cranelift-assembler-x64", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-assembler-x64-meta", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-bitset", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-control", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-entity", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-isle", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-jit", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-module", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-native", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-object", "Apache-2.0 WITH LLVM-exception"), - ("cranelift-srcgen", "Apache-2.0 WITH LLVM-exception"), ("foldhash", "Zlib"), ("mach2", "BSD-2-Clause OR MIT OR Apache-2.0"), - ("regalloc2", "Apache-2.0 WITH LLVM-exception"), - ("target-lexicon", "Apache-2.0 WITH LLVM-exception"), - ("wasmtime-jit-icache-coherence", "Apache-2.0 WITH LLVM-exception"), - ("wasmtime-math", "Apache-2.0 WITH LLVM-exception"), // tidy-alphabetical-end ]; From 8510865d64a70038357732cd26533b969c1ac892 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 18 Oct 2025 13:02:53 +0000 Subject: [PATCH 07/12] Add a couple of licenses that are can be used as MIT, Apache-2.0 or as both --- src/tools/tidy/src/deps.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index dd885e1e9d88b..622fa1d534f5d 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -31,10 +31,15 @@ const LICENSES: &[&str] = &[ "Apache-2.0", "Apache-2.0/MIT", "BSD-2-Clause OR Apache-2.0 OR MIT", // zerocopy + "BSD-2-Clause OR MIT OR Apache-2.0", + "BSD-3-Clause/MIT", + "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception", + "CC0-1.0 OR MIT-0 OR Apache-2.0", "ISC", "MIT / Apache-2.0", "MIT AND (MIT OR Apache-2.0)", "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)", // compiler-builtins + "MIT OR Apache-2.0 OR BSD-1-Clause", "MIT OR Apache-2.0 OR LGPL-2.1-or-later", // r-efi, r-efi-alloc; LGPL is not acceptable, but we use it under MIT OR Apache-2.0 "MIT OR Apache-2.0 OR Zlib", // tinyvec_macros "MIT OR Apache-2.0", @@ -171,9 +176,7 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ const EXCEPTIONS: ExceptionList = &[ // tidy-alphabetical-start ("arrayref", "BSD-2-Clause"), // rustc - ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), // rustc ("colored", "MPL-2.0"), // rustfmt - ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), // rustc ("foldhash", "Zlib"), // rustc ("option-ext", "MPL-2.0"), // cargo-miri (via `directories`) ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde) @@ -195,11 +198,7 @@ const EXCEPTIONS_CARGO: ExceptionList = &[ // tidy-alphabetical-start ("arrayref", "BSD-2-Clause"), ("bitmaps", "MPL-2.0+"), - ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"), - ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"), - ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"), ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), - ("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"), ("foldhash", "Zlib"), ("im-rc", "MPL-2.0+"), ("libz-rs-sys", "Zlib"), @@ -224,8 +223,6 @@ const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[ // tidy-alphabetical-start ("alloc-no-stdlib", "BSD-3-Clause"), ("alloc-stdlib", "BSD-3-Clause"), - ("brotli", "BSD-3-Clause/MIT"), - ("brotli-decompressor", "BSD-3-Clause/MIT"), ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), ("inferno", "CDDL-1.0"), ("option-ext", "MPL-2.0"), @@ -248,7 +245,6 @@ const EXCEPTIONS_RUSTBOOK: ExceptionList = &[ const EXCEPTIONS_CRANELIFT: ExceptionList = &[ // tidy-alphabetical-start ("foldhash", "Zlib"), - ("mach2", "BSD-2-Clause OR MIT OR Apache-2.0"), // tidy-alphabetical-end ]; From e7b96944dfd0c57d7ff4fb032a0578a330e74a1a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 18 Oct 2025 13:08:09 +0000 Subject: [PATCH 08/12] Add a separate licenses list for licenses that are fine in tools only With this the only remaining license exceptions are for licenses that are to copyleft to varying degrees. --- src/tools/tidy/src/deps.rs | 51 ++++++++++++++------------------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 622fa1d534f5d..db7751ea086c5 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -54,6 +54,20 @@ const LICENSES: &[&str] = &[ // tidy-alphabetical-end ]; +/// These are licenses that are allowed for rustc, tools, etc. But not for the runtime! +#[rustfmt::skip] +const LICENSES_TOOLS: &[&str] = &[ + // tidy-alphabetical-start + "(Apache-2.0 OR MIT) AND BSD-3-Clause", + "Apache-2.0 AND ISC", + "Apache-2.0 OR BSL-1.0", // BSL is not acceptble, but we use it under Apache-2.0 + "BSD-2-Clause", + "BSD-3-Clause", + "CC0-1.0", + "Zlib", + // tidy-alphabetical-end +]; + type ExceptionList = &'static [(&'static str, &'static str)]; #[derive(Clone, Copy)] @@ -175,11 +189,8 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ #[rustfmt::skip] const EXCEPTIONS: ExceptionList = &[ // tidy-alphabetical-start - ("arrayref", "BSD-2-Clause"), // rustc ("colored", "MPL-2.0"), // rustfmt - ("foldhash", "Zlib"), // rustc ("option-ext", "MPL-2.0"), // cargo-miri (via `directories`) - ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 // cargo/... (because of serde) // tidy-alphabetical-end ]; @@ -196,39 +207,22 @@ const EXCEPTIONS_STDLIB: ExceptionList = &[ const EXCEPTIONS_CARGO: ExceptionList = &[ // tidy-alphabetical-start - ("arrayref", "BSD-2-Clause"), ("bitmaps", "MPL-2.0+"), - ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), - ("foldhash", "Zlib"), ("im-rc", "MPL-2.0+"), - ("libz-rs-sys", "Zlib"), - ("ring", "Apache-2.0 AND ISC"), - ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 ("sized-chunks", "MPL-2.0+"), - ("subtle", "BSD-3-Clause"), - ("zlib-rs", "Zlib"), // tidy-alphabetical-end ]; const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ // tidy-alphabetical-start - ("foldhash", "Zlib"), - ("notify", "CC0-1.0"), ("option-ext", "MPL-2.0"), - ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0 - // tidy-alphabetical-end + // tidy-alphabetical-end ]; const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[ // tidy-alphabetical-start - ("alloc-no-stdlib", "BSD-3-Clause"), - ("alloc-stdlib", "BSD-3-Clause"), - ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), ("inferno", "CDDL-1.0"), ("option-ext", "MPL-2.0"), - ("ryu", "Apache-2.0 OR BSL-1.0"), - ("snap", "BSD-3-Clause"), - ("subtle", "BSD-3-Clause"), // tidy-alphabetical-end ]; @@ -238,15 +232,10 @@ const EXCEPTIONS_RUSTBOOK: ExceptionList = &[ ("cssparser-macros", "MPL-2.0"), ("dtoa-short", "MPL-2.0"), ("mdbook", "MPL-2.0"), - ("ryu", "Apache-2.0 OR BSL-1.0"), // tidy-alphabetical-end ]; -const EXCEPTIONS_CRANELIFT: ExceptionList = &[ - // tidy-alphabetical-start - ("foldhash", "Zlib"), - // tidy-alphabetical-end -]; +const EXCEPTIONS_CRANELIFT: ExceptionList = &[]; const EXCEPTIONS_GCC: ExceptionList = &[ // tidy-alphabetical-start @@ -255,9 +244,7 @@ const EXCEPTIONS_GCC: ExceptionList = &[ // tidy-alphabetical-end ]; -const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[ - ("ryu", "Apache-2.0 OR BSL-1.0"), // through serde. BSL is not acceptble, but we use it under Apache-2.0 -]; +const EXCEPTIONS_BOOTSTRAP: ExceptionList = &[]; const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[]; @@ -824,7 +811,7 @@ fn check_license_exceptions( } } } - if LICENSES.contains(license) { + if LICENSES.contains(license) || LICENSES_TOOLS.contains(license) { check.error(format!( "dependency exception `{name}` is not necessary. `{license}` is an allowed license" )); @@ -852,7 +839,7 @@ fn check_license_exceptions( continue; } }; - if !LICENSES.contains(&license.as_str()) { + if !LICENSES.contains(&license.as_str()) && !LICENSES_TOOLS.contains(&license.as_str()) { check.error(format!( "invalid license `{}` for package `{}` in workspace `{workspace}`", license, pkg.id From 6ab00baad0c935ea111388644591de8cd7ddc76e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 18 Oct 2025 13:08:28 +0000 Subject: [PATCH 09/12] Minor change --- src/tools/tidy/src/deps.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index db7751ea086c5..b2404edd8d0ed 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -109,17 +109,15 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ )), submodules: &[], }, - { - WorkspaceInfo { - path: "compiler/rustc_codegen_cranelift", - exceptions: EXCEPTIONS_CRANELIFT, - crates_and_deps: Some(( - &["rustc_codegen_cranelift"], - PERMITTED_CRANELIFT_DEPENDENCIES, - PERMITTED_CRANELIFT_DEPS_LOCATION, - )), - submodules: &[], - } + WorkspaceInfo { + path: "compiler/rustc_codegen_cranelift", + exceptions: EXCEPTIONS_CRANELIFT, + crates_and_deps: Some(( + &["rustc_codegen_cranelift"], + PERMITTED_CRANELIFT_DEPENDENCIES, + PERMITTED_CRANELIFT_DEPS_LOCATION, + )), + submodules: &[], }, WorkspaceInfo { path: "compiler/rustc_codegen_gcc", From 2ed1f47f7c3466e88dac60f5bfa3eaca1cc0068f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 18 Oct 2025 17:46:18 +0200 Subject: [PATCH 10/12] Fix typo Co-authored-by: Josh Triplett --- src/tools/tidy/src/deps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index b2404edd8d0ed..8555959774107 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -60,7 +60,7 @@ const LICENSES_TOOLS: &[&str] = &[ // tidy-alphabetical-start "(Apache-2.0 OR MIT) AND BSD-3-Clause", "Apache-2.0 AND ISC", - "Apache-2.0 OR BSL-1.0", // BSL is not acceptble, but we use it under Apache-2.0 + "Apache-2.0 OR BSL-1.0", // BSL is not acceptable, but we use it under Apache-2.0 "BSD-2-Clause", "BSD-3-Clause", "CC0-1.0", From dd70015f4692fc6eba5367ec399d9c50eb3f8268 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 18 Oct 2025 15:51:52 +0000 Subject: [PATCH 11/12] Deny all licenses that are not like MIT for the runtime libraries --- src/tools/tidy/src/deps.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 8555959774107..60ad88e7c4e14 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -19,21 +19,15 @@ mod proc_macro_deps; #[rustfmt::skip] const LICENSES: &[&str] = &[ // tidy-alphabetical-start - "(MIT OR Apache-2.0) AND Unicode-3.0", // unicode_ident (1.0.14) - "(MIT OR Apache-2.0) AND Unicode-DFS-2016", // unicode_ident (1.0.12) "0BSD OR MIT OR Apache-2.0", // adler2 license - "0BSD", "Apache-2.0 / MIT", "Apache-2.0 OR ISC OR MIT", "Apache-2.0 OR MIT", "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", // wasi license - "Apache-2.0 WITH LLVM-exception", - "Apache-2.0", "Apache-2.0/MIT", "BSD-2-Clause OR Apache-2.0 OR MIT", // zerocopy "BSD-2-Clause OR MIT OR Apache-2.0", "BSD-3-Clause/MIT", - "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception", "CC0-1.0 OR MIT-0 OR Apache-2.0", "ISC", "MIT / Apache-2.0", @@ -46,11 +40,8 @@ const LICENSES: &[&str] = &[ "MIT OR Zlib OR Apache-2.0", // miniz_oxide "MIT", "MIT/Apache-2.0", - "Unicode-3.0", // icu4x - "Unicode-DFS-2016", // tinystr "Unlicense OR MIT", "Unlicense/MIT", - "Zlib OR Apache-2.0 OR MIT", // tinyvec // tidy-alphabetical-end ]; @@ -59,11 +50,20 @@ const LICENSES: &[&str] = &[ const LICENSES_TOOLS: &[&str] = &[ // tidy-alphabetical-start "(Apache-2.0 OR MIT) AND BSD-3-Clause", + "(MIT OR Apache-2.0) AND Unicode-3.0", // unicode_ident (1.0.14) + "(MIT OR Apache-2.0) AND Unicode-DFS-2016", // unicode_ident (1.0.12) + "0BSD", "Apache-2.0 AND ISC", "Apache-2.0 OR BSL-1.0", // BSL is not acceptable, but we use it under Apache-2.0 + "Apache-2.0 WITH LLVM-exception", + "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", + "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception", "CC0-1.0", + "Unicode-3.0", // icu4x + "Unicode-DFS-2016", // tinystr + "Zlib OR Apache-2.0 OR MIT", // tinyvec "Zlib", // tidy-alphabetical-end ]; From ad67c9d581b7de937398ddea8671d41c9a96dfa0 Mon Sep 17 00:00:00 2001 From: relaxcn Date: Sun, 19 Oct 2025 23:58:04 +0800 Subject: [PATCH 12/12] fix: Use untrimmed line numbers for trimmed suggestions --- compiler/rustc_errors/src/emitter.rs | 4 +- compiler/rustc_errors/src/lib.rs | 71 ++++++++++++------- .../trimmed_multiline_suggestion.rs | 14 ++++ .../trimmed_multiline_suggestion.stderr | 25 +++++++ 4 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 tests/ui/error-emitter/trimmed_multiline_suggestion.rs create mode 100644 tests/ui/error-emitter/trimmed_multiline_suggestion.stderr diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 0d10f29d31d64..afdb582fea652 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2154,11 +2154,11 @@ impl HumanEmitter { assert!(!file_lines.lines.is_empty() || parts[0].span.is_dummy()); - let line_start = sm.lookup_char_pos(parts[0].span.lo()).line; + let line_start = sm.lookup_char_pos(parts[0].original_span.lo()).line; let mut lines = complete.lines(); if lines.clone().next().is_none() { // Account for a suggestion to completely remove a line(s) with whitespace (#94192). - let line_end = sm.lookup_char_pos(parts[0].span.hi()).line; + let line_end = sm.lookup_char_pos(parts[0].original_span.hi()).line; for line in line_start..=line_end { self.draw_line_num( &mut buffer, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8869799ce90d9..6bec65f2d538c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -224,6 +224,13 @@ pub struct SubstitutionPart { pub snippet: String, } +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub struct TrimmedSubstitutionPart { + pub original_span: Span, + pub span: Span, + pub snippet: String, +} + /// Used to translate between `Span`s and byte positions within a single output line in highlighted /// code of structured suggestions. #[derive(Debug, Clone, Copy)] @@ -233,6 +240,35 @@ pub(crate) struct SubstitutionHighlight { } impl SubstitutionPart { + /// Try to turn a replacement into an addition when the span that is being + /// overwritten matches either the prefix or suffix of the replacement. + fn trim_trivial_replacements(self, sm: &SourceMap) -> TrimmedSubstitutionPart { + let mut trimmed_part = TrimmedSubstitutionPart { + original_span: self.span, + span: self.span, + snippet: self.snippet, + }; + if trimmed_part.snippet.is_empty() { + return trimmed_part; + } + let Ok(snippet) = sm.span_to_snippet(trimmed_part.span) else { + return trimmed_part; + }; + + if let Some((prefix, substr, suffix)) = as_substr(&snippet, &trimmed_part.snippet) { + trimmed_part.span = Span::new( + trimmed_part.span.lo() + BytePos(prefix as u32), + trimmed_part.span.hi() - BytePos(suffix as u32), + trimmed_part.span.ctxt(), + trimmed_part.span.parent(), + ); + trimmed_part.snippet = substr.to_string(); + } + trimmed_part + } +} + +impl TrimmedSubstitutionPart { pub fn is_addition(&self, sm: &SourceMap) -> bool { !self.snippet.is_empty() && !self.replaces_meaningful_content(sm) } @@ -260,27 +296,6 @@ impl SubstitutionPart { sm.span_to_snippet(self.span) .map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty()) } - - /// Try to turn a replacement into an addition when the span that is being - /// overwritten matches either the prefix or suffix of the replacement. - fn trim_trivial_replacements(&mut self, sm: &SourceMap) { - if self.snippet.is_empty() { - return; - } - let Ok(snippet) = sm.span_to_snippet(self.span) else { - return; - }; - - if let Some((prefix, substr, suffix)) = as_substr(&snippet, &self.snippet) { - self.span = Span::new( - self.span.lo() + BytePos(prefix as u32), - self.span.hi() - BytePos(suffix as u32), - self.span.ctxt(), - self.span.parent(), - ); - self.snippet = substr.to_string(); - } - } } /// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect @@ -310,7 +325,8 @@ impl CodeSuggestion { pub(crate) fn splice_lines( &self, sm: &SourceMap, - ) -> Vec<(String, Vec, Vec>, ConfusionType)> { + ) -> Vec<(String, Vec, Vec>, ConfusionType)> + { // For the `Vec>` value, the first level of the vector // corresponds to the output snippet's lines, while the second level corresponds to the // substrings within that line that should be highlighted. @@ -428,12 +444,17 @@ impl CodeSuggestion { // or deleted code in order to point at the correct column *after* substitution. let mut acc = 0; let mut confusion_type = ConfusionType::None; - for part in &mut substitution.parts { + + let trimmed_parts = substitution + .parts + .into_iter() // If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the // suggestion and snippet to look as if we just suggested to add // `"b"`, which is typically much easier for the user to understand. - part.trim_trivial_replacements(sm); + .map(|part| part.trim_trivial_replacements(sm)) + .collect::>(); + for part in &trimmed_parts { let part_confusion = detect_confusion_type(sm, &part.snippet, part.span); confusion_type = confusion_type.combine(part_confusion); let cur_lo = sm.lookup_char_pos(part.span.lo()); @@ -521,7 +542,7 @@ impl CodeSuggestion { if highlights.iter().all(|parts| parts.is_empty()) { None } else { - Some((buf, substitution.parts, highlights, confusion_type)) + Some((buf, trimmed_parts, highlights, confusion_type)) } }) .collect() diff --git a/tests/ui/error-emitter/trimmed_multiline_suggestion.rs b/tests/ui/error-emitter/trimmed_multiline_suggestion.rs new file mode 100644 index 0000000000000..c0ae9af4e9b25 --- /dev/null +++ b/tests/ui/error-emitter/trimmed_multiline_suggestion.rs @@ -0,0 +1,14 @@ +//@ compile-flags: -Z ui-testing=no +fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + +fn main() { + let variable_name = 42; + function_with_lots_of_arguments( + variable_name, + variable_name, + variable_name, + variable_name, + variable_name, + ); + //~^^^^^^^ ERROR this function takes 6 arguments but 5 arguments were supplied [E0061] +} diff --git a/tests/ui/error-emitter/trimmed_multiline_suggestion.stderr b/tests/ui/error-emitter/trimmed_multiline_suggestion.stderr new file mode 100644 index 0000000000000..6e1ffb153bc2b --- /dev/null +++ b/tests/ui/error-emitter/trimmed_multiline_suggestion.stderr @@ -0,0 +1,25 @@ +error[E0061]: this function takes 6 arguments but 5 arguments were supplied + --> $DIR/trimmed_multiline_suggestion.rs:6:5 + | +6 | function_with_lots_of_arguments( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +7 | variable_name, +8 | variable_name, + | ------------- argument #2 of type `char` is missing + | +note: function defined here + --> $DIR/trimmed_multiline_suggestion.rs:2:4 + | +2 | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- +help: provide the argument + | +6 | function_with_lots_of_arguments( +7 | variable_name, +8 ~ /* char */, +9 ~ variable_name, + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0061`.