From 27c40cbf01f9edf8d7c534db6d22687cdeff6688 Mon Sep 17 00:00:00 2001 From: RGBCube Date: Sat, 12 Jul 2025 08:31:17 +0000 Subject: [PATCH 1/4] Fix clippy lints (#75) --- cstree/examples/math.rs | 2 +- cstree/examples/s_expressions.rs | 2 +- cstree/src/interning/lasso_compat/token_interner.rs | 2 +- cstree/src/syntax/node.rs | 8 +++----- cstree/src/syntax/text.rs | 10 ++++------ cstree/src/syntax/token.rs | 4 ++-- cstree/tests/it/basic.rs | 10 +++++----- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/cstree/examples/math.rs b/cstree/examples/math.rs index ae44a4e..a98f6a2 100644 --- a/cstree/examples/math.rs +++ b/cstree/examples/math.rs @@ -114,7 +114,7 @@ fn print(indent: usize, element: SyntaxElementRef<'_>, resolver: &impl Resolver) print!("{:indent$}", "", indent = indent); match element { NodeOrToken::Node(node) => { - println!("- {:?}", kind); + println!("- {kind:?}"); for child in node.children_with_tokens() { print(indent + 2, child, resolver); } diff --git a/cstree/examples/s_expressions.rs b/cstree/examples/s_expressions.rs index dcbe7a3..f37b3fb 100644 --- a/cstree/examples/s_expressions.rs +++ b/cstree/examples/s_expressions.rs @@ -392,7 +392,7 @@ nan let root = parse.root(); let resolver = &parse.resolver; let res = root.sexps().map(|it| it.eval(resolver)).collect::>(); - eprintln!("{:?}", res); + eprintln!("{res:?}"); assert_eq!(res, vec![Some(92), Some(92), None, None, Some(92),]) } diff --git a/cstree/src/interning/lasso_compat/token_interner.rs b/cstree/src/interning/lasso_compat/token_interner.rs index 35b8e75..248baa4 100644 --- a/cstree/src/interning/lasso_compat/token_interner.rs +++ b/cstree/src/interning/lasso_compat/token_interner.rs @@ -15,7 +15,7 @@ const DEFAULT_STRING_CAPACITY: usize = 512; /// Default memory in bytes that the interner will initially allocate space for. /// Value recommended by the author of `lasso`. -const DEFAULT_BYTE_CAPACITY: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(4096) }; +const DEFAULT_BYTE_CAPACITY: NonZeroUsize = NonZeroUsize::new(4096).unwrap(); macro_rules! impl_traits { (for $interner:ty $(, if #[cfg(feature = $feature:literal)])?) => { diff --git a/cstree/src/syntax/node.rs b/cstree/src/syntax/node.rs index 429b1da..356e0f6 100644 --- a/cstree/src/syntax/node.rs +++ b/cstree/src/syntax/node.rs @@ -851,9 +851,7 @@ impl SyntaxNode { let range = self.text_range(); assert!( range.start() <= offset && offset <= range.end(), - "Bad offset: range {:?} offset {:?}", - range, - offset + "Bad offset: range {range:?} offset {offset:?}" ); if range.is_empty() { return TokenAtOffset::None; @@ -940,7 +938,7 @@ impl GreenNode { &self, start_index: usize, mut offset: TextSize, - ) -> impl Iterator { + ) -> impl Iterator, (usize, TextSize))> { self.children() .skip(start_index) .enumerate() @@ -956,7 +954,7 @@ impl GreenNode { &self, end_index: usize, mut offset: TextSize, - ) -> impl Iterator { + ) -> impl Iterator, (usize, TextSize))> { self.children() .take(end_index) .rev() diff --git a/cstree/src/syntax/text.rs b/cstree/src/syntax/text.rs index b613556..f70fb77 100644 --- a/cstree/src/syntax/text.rs +++ b/cstree/src/syntax/text.rs @@ -201,11 +201,9 @@ impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, } } +#[inline] fn found(res: Result<(), T>) -> Option { - match res { - Ok(()) => None, - Err(it) => Some(it), - } + res.err() } impl + ?Sized, S: Syntax, D> fmt::Debug for SyntaxText<'_, '_, I, S, D> { @@ -429,9 +427,9 @@ mod tests { let t2 = t2.resolve_text(&resolver); let expected = t1.to_string() == t2.to_string(); let actual = t1 == t2; - assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (SyntaxText)", t1, t2); + assert_eq!(expected, actual, "`{t1}` (SyntaxText) `{t2}` (SyntaxText)"); let actual = t1 == t2.to_string().as_str(); - assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (&str)", t1, t2); + assert_eq!(expected, actual, "`{t1}` (SyntaxText) `{t2}` (&str)"); } fn check(t1: &[&str], t2: &[&str]) { do_check(t1, t2); diff --git a/cstree/src/syntax/token.rs b/cstree/src/syntax/token.rs index bcd1ff9..65dd902 100644 --- a/cstree/src/syntax/token.rs +++ b/cstree/src/syntax/token.rs @@ -58,13 +58,13 @@ impl SyntaxToken { write!(target, "{:?}@{:?}", self.kind(), self.text_range())?; let text = self.resolve_text(resolver); if text.len() < 25 { - return write!(target, " {:?}", text); + return write!(target, " {text:?}"); } for idx in 21..25 { if text.is_char_boundary(idx) { let text = format!("{} ...", &text[..idx]); - return write!(target, " {:?}", text); + return write!(target, " {text:?}"); } } unreachable!() diff --git a/cstree/tests/it/basic.rs b/cstree/tests/it/basic.rs index c02fc3b..207bdab 100644 --- a/cstree/tests/it/basic.rs +++ b/cstree/tests/it/basic.rs @@ -149,18 +149,18 @@ fn inline_resolver() { let leaf1_0 = leaf1_0.into_token().unwrap(); assert_eq!(leaf1_0.text(), "1.0"); assert_eq!(leaf1_0.text_range(), TextRange::at(6.into(), 3.into())); - assert_eq!(format!("{}", leaf1_0), leaf1_0.text()); - assert_eq!(format!("{:?}", leaf1_0), "SyntaxKind(5)@6..9 \"1.0\""); + assert_eq!(format!("{leaf1_0}"), leaf1_0.text()); + assert_eq!(format!("{leaf1_0:?}"), "SyntaxKind(5)@6..9 \"1.0\""); } { let node2 = tree.children().nth(2).unwrap(); assert_eq!(node2.text(), "2.02.12.2"); let resolver = node2.resolver(); assert_eq!(node2.resolve_text(resolver.as_ref()), node2.text()); - assert_eq!(format!("{}", node2).as_str(), node2.text()); - assert_eq!(format!("{:?}", node2), "SyntaxKind(6)@9..18"); + assert_eq!(format!("{node2}").as_str(), node2.text()); + assert_eq!(format!("{node2:?}"), "SyntaxKind(6)@9..18"); assert_eq!( - format!("{:#?}", node2), + format!("{node2:#?}"), r#"SyntaxKind(6)@9..18 SyntaxKind(7)@9..12 "2.0" SyntaxKind(8)@12..15 "2.1" From a8ebf2980861443eb75f9ccffee3fddc2f74f209 Mon Sep 17 00:00:00 2001 From: RGBCube Date: Sat, 12 Jul 2025 15:27:24 +0000 Subject: [PATCH 2/4] Fix overly restrictive derive(Clone)s (#74) Previously, we couldn't clone `SyntaxNodeChildren` if D wasn't `Clone`, but `D` doesn't get cloned, so this is misleading and overly restrictive. This patch fixes that & other structs that did the same thing. --- cstree/src/syntax/iter.rs | 54 +++++++++++++++++++++++++++++++++++++-- cstree/src/syntax/text.rs | 24 +++++++++++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/cstree/src/syntax/iter.rs b/cstree/src/syntax/iter.rs index 74926d8..fdc0ae1 100644 --- a/cstree/src/syntax/iter.rs +++ b/cstree/src/syntax/iter.rs @@ -66,12 +66,21 @@ impl ExactSizeIterator for Iter<'_> { impl FusedIterator for Iter<'_> {} /// An iterator over the child nodes of a [`SyntaxNode`]. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct SyntaxNodeChildren<'n, S: Syntax, D: 'static = ()> { inner: Iter<'n>, parent: &'n SyntaxNode, } +impl Clone for SyntaxNodeChildren<'_, S, D> { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + parent: self.parent, + } + } +} + impl<'n, S: Syntax, D> SyntaxNodeChildren<'n, S, D> { #[inline] pub(super) fn new(parent: &'n SyntaxNode) -> Self { @@ -118,12 +127,21 @@ impl ExactSizeIterator for SyntaxNodeChildren<'_, S, D> { impl FusedIterator for SyntaxNodeChildren<'_, S, D> {} /// An iterator over the children of a [`SyntaxNode`]. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct SyntaxElementChildren<'n, S: Syntax, D: 'static = ()> { inner: Iter<'n>, parent: &'n SyntaxNode, } +impl Clone for SyntaxElementChildren<'_, S, D> { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + parent: self.parent, + } + } +} + impl<'n, S: Syntax, D> SyntaxElementChildren<'n, S, D> { #[inline] pub(super) fn new(parent: &'n SyntaxNode) -> Self { @@ -166,3 +184,35 @@ impl ExactSizeIterator for SyntaxElementChildren<'_, S, D> { } } impl FusedIterator for SyntaxElementChildren<'_, S, D> {} + +#[cfg(test)] +#[allow(dead_code)] +mod tests { + use super::*; + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct DummyKind; + + impl Syntax for DummyKind { + fn from_raw(_: crate::RawSyntaxKind) -> Self { + unreachable!() + } + + fn into_raw(self) -> crate::RawSyntaxKind { + unreachable!() + } + + fn static_text(self) -> Option<&'static str> { + unreachable!() + } + } + + struct NotClone; + + fn assert_clone() {} + + fn test_impls_clone() { + assert_clone::>(); + assert_clone::>(); + } +} diff --git a/cstree/src/syntax/text.rs b/cstree/src/syntax/text.rs index f70fb77..5525e4e 100644 --- a/cstree/src/syntax/text.rs +++ b/cstree/src/syntax/text.rs @@ -40,13 +40,20 @@ use crate::{ /// let sub = text.slice(2.into()..5.into()); /// assert_eq!(sub, "748"); /// ``` -#[derive(Clone)] pub struct SyntaxText<'n, 'i, I: ?Sized, S: Syntax, D: 'static = ()> { node: &'n SyntaxNode, range: TextRange, resolver: &'i I, } +impl Clone for SyntaxText<'_, '_, I, S, D> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for SyntaxText<'_, '_, I, S, D> {} + impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, S, D> { pub(crate) fn new(node: &'n SyntaxNode, resolver: &'i I) -> Self { let range = node.text_range(); @@ -376,7 +383,7 @@ mod private { #[cfg(test)] mod tests { - use crate::{build::GreenNodeBuilder, RawSyntaxKind}; + use crate::{build::GreenNodeBuilder, interning::TokenInterner, RawSyntaxKind}; use super::*; @@ -448,4 +455,17 @@ mod tests { check(&["{", "abc", "}"], &["{", "123", "}", "{"]); check(&["{", "abc", "}ab"], &["{", "abc", "}", "ab"]); } + + #[allow(dead_code)] + mod impl_asserts { + use super::*; + + struct NotClone; + + fn assert_copy() {} + + fn test_impls_copy() { + assert_copy::>(); + } + } } From c35e88c3da3204ab9377c454942fcfcfb057e12e Mon Sep 17 00:00:00 2001 From: RGBCube Date: Tue, 29 Jul 2025 18:59:54 +0300 Subject: [PATCH 3/4] text: add SyntaxText::rfind_char --- cstree/src/syntax/text.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cstree/src/syntax/text.rs b/cstree/src/syntax/text.rs index 5525e4e..be08600 100644 --- a/cstree/src/syntax/text.rs +++ b/cstree/src/syntax/text.rs @@ -91,6 +91,18 @@ impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, found(res) } + /// If `self.contains_char(c)`, returns `Some(pos)`, where `pos` is the byte position of the + /// last appearance of `c`. Otherwise, returns `None`. + pub fn rfind_char(&self, c: char) -> Option { + let mut acc: TextSize = 0.into(); + let mut res = None; + self.for_each_chunk(|chunk| { + res = chunk.rfind(c).map(|pos| acc + TextSize::from(pos as u32)).or(res); + acc += TextSize::of(chunk); + }); + res + } + /// If `offset < self.len()`, returns `Some(c)`, where `c` is the first `char` at or after /// `offset` (in bytes). Otherwise, returns `None`. pub fn char_at(&self, offset: TextSize) -> Option { From 35c2799059bb98fcef4e89f1c9fa381e73439b00 Mon Sep 17 00:00:00 2001 From: RGBCube Date: Tue, 29 Jul 2025 19:26:22 +0300 Subject: [PATCH 4/4] treewide: specify lifetimes correctly don't overrestrict lifetimes, prefer `use<...>` to prevent `impl Trait` from capturing too much --- cstree/src/syntax/element.rs | 2 +- cstree/src/syntax/resolved.rs | 2 +- cstree/src/syntax/text.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cstree/src/syntax/element.rs b/cstree/src/syntax/element.rs index bef407a..27fbdc4 100644 --- a/cstree/src/syntax/element.rs +++ b/cstree/src/syntax/element.rs @@ -292,7 +292,7 @@ impl<'a, S: Syntax, D> SyntaxElementRef<'a, S, D> { /// Returns an iterator along the chain of parents of this node. #[inline] - pub fn ancestors(&self) -> impl Iterator> { + pub fn ancestors(&self) -> impl Iterator> + use<'a, S, D> { match self { NodeOrToken::Node(it) => it.ancestors(), NodeOrToken::Token(it) => it.parent().ancestors(), diff --git a/cstree/src/syntax/resolved.rs b/cstree/src/syntax/resolved.rs index 500d611..4c97301 100644 --- a/cstree/src/syntax/resolved.rs +++ b/cstree/src/syntax/resolved.rs @@ -713,7 +713,7 @@ impl<'a, S: Syntax, D> ResolvedElementRef<'a, S, D> { /// Returns an iterator along the chain of parents of this node. #[inline] - pub fn ancestors(&self) -> impl Iterator> { + pub fn ancestors(&self) -> impl Iterator> + use<'a, S, D> { match self { NodeOrToken::Node(it) => it.ancestors(), NodeOrToken::Token(it) => it.parent().ancestors(), diff --git a/cstree/src/syntax/text.rs b/cstree/src/syntax/text.rs index 5525e4e..67bb909 100644 --- a/cstree/src/syntax/text.rs +++ b/cstree/src/syntax/text.rs @@ -150,7 +150,7 @@ impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, /// See also [`fold_chunks`](SyntaxText::fold_chunks) for folds that always succeed. pub fn try_fold_chunks(&self, init: T, mut f: F) -> Result where - F: FnMut(T, &str) -> Result, + F: FnMut(T, &'i str) -> Result, { self.tokens_with_ranges().try_fold(init, move |acc, (token, range)| { f(acc, &token.resolve_text(self.resolver)[range]) @@ -195,7 +195,7 @@ impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, self.fold_chunks((), |(), chunk| f(chunk)) } - fn tokens_with_ranges(&self) -> impl Iterator, TextRange)> { + fn tokens_with_ranges(&self) -> impl Iterator, TextRange)> + use<'i, 'n, I, S, D> { let text_range = self.range; self.node .descendants_with_tokens()