fix(rust): more clippy fixes in generate

Additional clippy lints fire when certain modules are temporarily made
public, i.e. for testing purposes. Fixing these reduces clutter and
helps with internal development.
This commit is contained in:
Will Lillis 2026-06-04 18:37:00 -04:00
parent 4154ff23c0
commit 4455da7102
10 changed files with 91 additions and 4 deletions

View file

@ -93,6 +93,7 @@ pub struct BitVec {
}
impl BitVec {
#[must_use]
pub const fn new() -> Self {
Self {
data: ptr::NonNull::dangling().as_ptr(),
@ -101,6 +102,7 @@ impl BitVec {
}
}
#[must_use]
pub fn with_capacity(n_bits: usize) -> Self {
let n_words = n_bits.div_ceil(64);
if n_words == 0 {
@ -120,6 +122,7 @@ impl BitVec {
/// View the in-use words as a slice.
#[inline]
#[must_use]
pub const fn as_slice(&self) -> &[u64] {
let n = self.words_in_use();
if n == 0 {
@ -142,10 +145,13 @@ impl BitVec {
unsafe { std::slice::from_raw_parts_mut(self.data, n) }
}
#[must_use]
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
self.num_bits as usize
}
#[must_use]
pub fn get(&self, index: usize) -> Option<bool> {
if index >= self.num_bits as usize {
return None;
@ -206,6 +212,7 @@ impl BitVec {
self.num_bits = new_len as u32;
}
#[must_use]
pub fn last(&self) -> Option<bool> {
if self.num_bits == 0 {
return None;
@ -380,6 +387,7 @@ pub struct SetBitsIter<'a> {
}
impl<'a> SetBitsIter<'a> {
#[must_use]
pub fn new(data: &'a [u64]) -> Self {
Self {
data,

View file

@ -20,6 +20,7 @@ pub struct CoincidentTokenIndex<'a> {
}
impl<'a> CoincidentTokenIndex<'a> {
#[must_use]
pub fn new(table: &ParseTable, lexical_grammar: &'a LexicalGrammar) -> Self {
let n = lexical_grammar.variables.len();
let row_words = n.div_ceil(64);
@ -63,10 +64,12 @@ impl<'a> CoincidentTokenIndex<'a> {
result
}
#[must_use]
pub fn states_with(&self, a: Symbol, b: Symbol) -> &[ParseStateId] {
&self.entries[self.index(a.index, b.index)]
}
#[must_use]
pub fn contains(&self, a: Symbol, b: Symbol) -> bool {
let bit_index = a.index * self.n + b.index;
self.contains_bits[bit_index / 64] & (1u64 << (bit_index % 64)) != 0

View file

@ -53,9 +53,10 @@ pub struct ParseItem<'a> {
pub has_preceding_inherited_fields: bool,
}
/// A [`ParseItemSet`] represents a set of in-progress matches of productions in a
/// grammar, and for each in-progress match, a set of "lookaheads" - tokens that
/// are allowed to *follow* the in-progress rule. This object corresponds directly
/// Represents a set of in-progress matches of productions in a grammar.
///
/// For each in-progress match, a set of "lookaheads" (tokens that are allowed to
/// *follow* the in-progress rule) are included. This object corresponds directly
/// to a state in the final parse table.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct ParseItemSet<'a> {
@ -95,6 +96,7 @@ pub struct ParseItemSetDisplay<'a>(
);
impl<'a> ParseItem<'a> {
#[must_use]
pub fn start() -> Self {
ParseItem {
variable_index: u32::MAX,
@ -104,23 +106,28 @@ impl<'a> ParseItem<'a> {
}
}
#[must_use]
pub fn step(&self) -> Option<&'a ProductionStep> {
self.production.steps.get(self.step_index as usize)
}
#[must_use]
pub fn symbol(&self) -> Option<Symbol> {
self.step().map(|step| step.symbol)
}
#[must_use]
pub fn associativity(&self) -> Option<Associativity> {
self.prev_step().and_then(|step| step.associativity)
}
#[must_use]
pub fn precedence(&self) -> &Precedence {
self.prev_step()
.map_or(&Precedence::None, |step| &step.precedence)
}
#[must_use]
pub fn prev_step(&self) -> Option<&'a ProductionStep> {
if self.step_index > 0 {
Some(&self.production.steps[self.step_index as usize - 1])
@ -152,6 +159,7 @@ impl<'a> ParseItem<'a> {
/// Create an item identical to this one, but with a different production.
/// This is used when dynamically "inlining" certain symbols in a production.
#[must_use]
pub const fn substitute_production(&self, production: &'a Production) -> Self {
let mut result = *self;
result.production = production;
@ -178,6 +186,7 @@ impl<'a> ParseItemSet<'a> {
}
}
#[must_use]
pub fn core(&self) -> ParseItemSetCore<'a> {
ParseItemSetCore {
entries: self.entries.iter().map(|e| e.item).collect(),

View file

@ -38,6 +38,7 @@ fn find_or_push<T: Eq>(vector: &mut Vec<T>, value: T) {
}
impl<'a> ParseItemSetBuilder<'a> {
#[must_use]
pub fn new(
syntax_grammar: &'a SyntaxGrammar,
lexical_grammar: &'a LexicalGrammar,
@ -254,6 +255,7 @@ impl<'a> ParseItemSetBuilder<'a> {
result
}
#[must_use]
pub fn transitive_closure(&self, item_set: &ParseItemSet<'a>) -> ParseItemSet<'a> {
let mut result = ParseItemSet::default();
for entry in &item_set.entries {
@ -278,15 +280,18 @@ impl<'a> ParseItemSetBuilder<'a> {
result
}
#[must_use]
pub fn first_set(&self, symbol: &Symbol) -> &TokenSet {
&self.first_sets[symbol]
}
#[must_use]
pub fn reserved_first_set(&self, symbol: &Symbol) -> Option<&TokenSet> {
let id = *self.reserved_first_sets.get(symbol)?;
Some(&self.syntax_grammar.reserved_word_sets[id.0])
}
#[must_use]
pub fn last_set(&self, symbol: &Symbol) -> &TokenSet {
&self.last_sets[symbol]
}

View file

@ -47,6 +47,7 @@ impl<'a> TokenConflictMap<'a> {
///
/// This analyzes the possible kinds of overlap between each pair of tokens and stores
/// them in a matrix.
#[must_use]
pub fn new(grammar: &'a LexicalGrammar, following_tokens: Vec<TokenSet>) -> Self {
let mut cursor = NfaCursor::new(&grammar.nfa, Vec::new());
let starting_chars = get_starting_chars(&mut cursor, grammar);
@ -117,6 +118,7 @@ impl<'a> TokenConflictMap<'a> {
/// Does token `i` match any strings that token `j` also matches, such that token `i`
/// is preferred over token `j`?
#[must_use]
pub fn has_same_conflict_status(&self, a: usize, b: usize, other: usize) -> bool {
let left = &self.status_matrix[matrix_index(self.n, a, other)];
let right = &self.status_matrix[matrix_index(self.n, b, other)];
@ -124,6 +126,7 @@ impl<'a> TokenConflictMap<'a> {
}
/// Does token `i` match any strings that token `j` does *not* match?
#[must_use]
pub fn does_match_different_string(&self, i: usize, j: usize) -> bool {
self.status_matrix[matrix_index(self.n, i, j)]
.contains(TokenConflictStatus::MATCHES_DIFFERENT_STRING)
@ -132,12 +135,14 @@ impl<'a> TokenConflictMap<'a> {
/// Does token `i` match any strings that token `j` also matches, where
/// token `i` is preferred over token `j`?
#[inline]
#[must_use]
pub fn does_match_same_string(&self, i: usize, j: usize) -> bool {
self.status_matrix[matrix_index(self.n, i, j)]
.contains(TokenConflictStatus::MATCHES_SAME_STRING)
}
#[inline]
#[must_use]
pub fn does_conflict(&self, i: usize, j: usize) -> bool {
debug_assert!(i < self.n && j < self.n, "token indices out of bounds");
// Safety: i < n and j < n imply n*i+j < n*n == status_matrix.len().
@ -150,12 +155,14 @@ impl<'a> TokenConflictMap<'a> {
}
/// Does token `i` match any strings that are *prefixes* of strings matched by `j`?
#[expect(dead_code)]
#[inline]
#[must_use]
#[allow(dead_code)]
pub fn does_match_prefix(&self, i: usize, j: usize) -> bool {
self.status_matrix[matrix_index(self.n, i, j)].contains(TokenConflictStatus::MATCHES_PREFIX)
}
#[must_use]
pub fn does_match_shorter_or_longer(&self, i: usize, j: usize) -> bool {
let entry = self.status_matrix[matrix_index(self.n, i, j)];
let reverse_entry = self.status_matrix[matrix_index(self.n, j, i)];
@ -165,6 +172,7 @@ impl<'a> TokenConflictMap<'a> {
}
#[inline]
#[must_use]
pub fn does_overlap(&self, i: usize, j: usize) -> bool {
self.status_matrix[matrix_index(self.n, i, j)].intersects(
TokenConflictStatus::DOES_MATCH_SEPARATORS
@ -174,6 +182,7 @@ impl<'a> TokenConflictMap<'a> {
)
}
#[must_use]
pub fn prefer_token(grammar: &LexicalGrammar, left: (i32, usize), right: (i32, usize)) -> bool {
match left.0.cmp(&right.0) {
Ordering::Less => false,
@ -189,6 +198,7 @@ impl<'a> TokenConflictMap<'a> {
}
}
#[must_use]
pub fn prefer_transition(
grammar: &LexicalGrammar,
t: &NfaTransition,

View file

@ -140,6 +140,7 @@ impl ProductionStep {
}
}
#[must_use]
pub fn with_prec(
mut self,
precedence: Precedence,
@ -150,6 +151,7 @@ impl ProductionStep {
self
}
#[must_use]
pub fn with_alias(mut self, value: &str, is_named: bool) -> Self {
self.alias = Some(Alias {
value: value.to_string(),
@ -158,6 +160,7 @@ impl ProductionStep {
self
}
#[must_use]
pub fn with_field_name(mut self, name: &str) -> Self {
self.field_name = Some(name.to_string());
self
@ -165,6 +168,7 @@ impl ProductionStep {
}
impl Production {
#[must_use]
pub fn first_symbol(&self) -> Option<Symbol> {
self.steps.first().map(|s| s.symbol)
}
@ -172,6 +176,7 @@ impl Production {
#[cfg(test)]
impl Variable {
#[must_use]
pub fn named(name: &str, rule: Rule) -> Self {
Self {
name: name.to_string(),
@ -180,6 +185,7 @@ impl Variable {
}
}
#[must_use]
pub fn auxiliary(name: &str, rule: Rule) -> Self {
Self {
name: name.to_string(),
@ -188,6 +194,7 @@ impl Variable {
}
}
#[must_use]
pub fn hidden(name: &str, rule: Rule) -> Self {
Self {
name: name.to_string(),
@ -196,6 +203,7 @@ impl Variable {
}
}
#[must_use]
pub fn anonymous(name: &str, rule: Rule) -> Self {
Self {
name: name.to_string(),
@ -206,6 +214,7 @@ impl Variable {
}
impl VariableType {
#[must_use]
pub fn is_visible(self) -> bool {
self == Self::Named || self == Self::Anonymous
}
@ -228,6 +237,7 @@ impl LexicalGrammar {
})
}
#[must_use]
pub fn variable_index_for_nfa_state(&self, state_id: u32) -> usize {
// The NFA is built in reverse (accept state first, entry state last), so
// each variable's `start_state` is the last (highest) NFA state allocated
@ -238,16 +248,19 @@ impl LexicalGrammar {
}
impl SyntaxVariable {
#[must_use]
pub fn is_auxiliary(&self) -> bool {
self.kind == VariableType::Auxiliary
}
#[must_use]
pub fn is_hidden(&self) -> bool {
self.kind == VariableType::Hidden || self.kind == VariableType::Auxiliary
}
}
impl InlinedProductionMap {
#[must_use]
pub fn inlined_productions<'a>(
&'a self,
production: &Production,

View file

@ -52,6 +52,7 @@ const END: u32 = char::MAX as u32 + 1;
impl CharacterSet {
/// Create a character set with a single character.
#[must_use]
pub const fn empty() -> Self {
Self { ranges: Vec::new() }
}
@ -72,6 +73,7 @@ impl CharacterSet {
}
/// Create a character set with a single character.
#[must_use]
#[expect(
clippy::single_range_in_vec_init,
reason = "Vec is the backing store for CharacterSet"
@ -84,6 +86,7 @@ impl CharacterSet {
/// Create a character set containing all characters *not* present
/// in this character set.
#[must_use]
pub fn negate(mut self) -> Self {
let mut i = 0;
let mut previous_end = 0;
@ -104,16 +107,20 @@ impl CharacterSet {
self
}
#[must_use]
pub fn add_char(mut self, c: char) -> Self {
self.add_int_range(0, c as u32, c as u32 + 1);
self
}
#[must_use]
pub fn add_range(mut self, start: char, end: char) -> Self {
self.add_int_range(0, start as u32, end as u32 + 1);
self
}
#[must_use]
#[allow(clippy::should_implement_trait)]
pub fn add(mut self, other: &Self) -> Self {
let mut index = 0;
for range in &other.ranges {
@ -152,6 +159,7 @@ impl CharacterSet {
i
}
#[must_use]
pub fn does_intersect(&self, other: &Self) -> bool {
let mut left_ranges = self.ranges.iter();
let mut right_ranges = other.ranges.iter();
@ -172,6 +180,7 @@ impl CharacterSet {
/// Get the set of characters that are present in both this set
/// and the other set. Remove those common characters from both
/// of the operands.
#[allow(clippy::return_self_not_must_use)]
pub fn remove_intersection(&mut self, other: &mut Self) -> Self {
let mut intersection = Vec::new();
let mut left_i = 0;
@ -286,6 +295,7 @@ impl CharacterSet {
/// Produces a `CharacterSet` containing every character in `self` that is not present in
/// `other`.
#[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
pub fn difference(mut self, mut other: Self) -> Self {
self.remove_intersection(&mut other);
self
@ -307,6 +317,7 @@ impl CharacterSet {
self.char_codes().filter_map(char::from_u32)
}
#[must_use]
pub const fn range_count(&self) -> usize {
self.ranges.len()
}
@ -319,12 +330,14 @@ impl CharacterSet {
})
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
/// Get a reduced list of character ranges, assuming that a given
/// set of characters can be safely ignored.
#[must_use]
pub fn simplify_ignoring(&self, ruled_out_characters: &Self) -> Self {
let mut prev_range: Option<Range<u32>> = None;
Self {
@ -356,6 +369,7 @@ impl CharacterSet {
}
}
#[must_use]
pub fn contains_codepoint_range(&self, seek_range: Range<u32>) -> bool {
let ix = match self.ranges.binary_search_by(|probe| {
if probe.end <= seek_range.start {
@ -373,6 +387,7 @@ impl CharacterSet {
.is_some_and(|range| range.start <= seek_range.start && range.end >= seek_range.end)
}
#[must_use]
pub fn contains(&self, c: char) -> bool {
self.contains_codepoint_range(c as u32..c as u32 + 1)
}
@ -438,6 +453,7 @@ impl Nfa {
Self { states: Vec::new() }
}
#[must_use]
pub fn last_state_id(&self) -> u32 {
assert!(!self.states.is_empty());
self.states.len() as u32 - 1
@ -456,6 +472,7 @@ impl fmt::Debug for Nfa {
}
impl<'a> NfaCursor<'a> {
#[must_use]
pub fn new(nfa: &'a Nfa, mut states: Vec<u32>) -> Self {
let mut result = Self {
nfa,
@ -478,6 +495,7 @@ impl<'a> NfaCursor<'a> {
self.raw_transitions().map(|t| (t.0, t.1))
}
#[must_use]
pub fn transitions(&self) -> Vec<NfaTransition> {
Self::group_transitions(self.raw_transitions())
}
@ -485,6 +503,7 @@ impl<'a> NfaCursor<'a> {
/// Like [`transitions()`](Self::transitions) but also returns whether any raw NFA transition
/// is a separator. This is computed in the same pass, avoiding a second
/// iteration over `state_ids` for callers that need both.
#[must_use]
pub fn transitions_and_any_sep(&self) -> (Vec<NfaTransition>, bool) {
let mut any_sep = false;
let result =

View file

@ -128,6 +128,7 @@ impl Console {
#[rquickjs::methods]
impl Console {
#[must_use]
#[qjs(constructor)]
pub const fn new() -> Self {
Console {}

View file

@ -111,24 +111,28 @@ impl Ord for TokenSet {
}
impl Rule {
#[must_use]
pub fn field(name: String, content: Self) -> Self {
add_metadata(content, move |params| {
params.field_name = Some(name);
})
}
#[must_use]
pub fn alias(content: Self, value: String, is_named: bool) -> Self {
add_metadata(content, move |params| {
params.alias = Some(Alias { value, is_named });
})
}
#[must_use]
pub fn token(content: Self) -> Self {
add_metadata(content, |params| {
params.is_token = true;
})
}
#[must_use]
pub fn immediate_token(content: Self) -> Self {
add_metadata(content, |params| {
params.is_token = true;
@ -136,12 +140,14 @@ impl Rule {
})
}
#[must_use]
pub fn prec(value: Precedence, content: Self) -> Self {
add_metadata(content, |params| {
params.precedence = value;
})
}
#[must_use]
pub fn prec_left(value: Precedence, content: Self) -> Self {
add_metadata(content, |params| {
params.associativity = Some(Associativity::Left);
@ -149,6 +155,7 @@ impl Rule {
})
}
#[must_use]
pub fn prec_right(value: Precedence, content: Self) -> Self {
add_metadata(content, |params| {
params.associativity = Some(Associativity::Right);
@ -156,16 +163,19 @@ impl Rule {
})
}
#[must_use]
pub fn prec_dynamic(value: i32, content: Self) -> Self {
add_metadata(content, |params| {
params.dynamic_precedence = value;
})
}
#[must_use]
pub fn repeat(rule: Self) -> Self {
Self::Repeat(Box::new(rule))
}
#[must_use]
pub fn choice(rules: Vec<Self>) -> Self {
let mut elements = Vec::with_capacity(rules.len());
for rule in rules {
@ -174,6 +184,7 @@ impl Rule {
Self::Choice(elements)
}
#[must_use]
pub const fn seq(rules: Vec<Self>) -> Self {
Self::Seq(rules)
}
@ -321,6 +332,7 @@ impl TokenSet {
}
}
#[must_use]
pub fn with_capacity(n_terminals: usize, n_externals: usize) -> Self {
Self {
terminal_bits: BitVec::with_capacity(n_terminals),
@ -347,6 +359,7 @@ impl TokenSet {
}
#[inline]
#[must_use]
pub fn contains(&self, symbol: &Symbol) -> bool {
match symbol.kind {
SymbolType::NonTerminal => panic!("Cannot store non-terminals in a TokenSet"),
@ -358,12 +371,14 @@ impl TokenSet {
}
#[inline]
#[must_use]
pub fn contains_terminal(&self, index: usize) -> bool {
self.terminal_bits.get(index).unwrap_or(false)
}
/// Raw u64 word slice backing the terminal bitset.
#[inline]
#[must_use]
pub const fn terminal_bits_words(&self) -> &[u64] {
self.terminal_bits.as_slice()
}
@ -420,6 +435,7 @@ impl TokenSet {
false
}
#[must_use]
pub fn is_empty(&self) -> bool {
!self.eof
&& !self.end_of_nonterminal_extra
@ -427,6 +443,7 @@ impl TokenSet {
&& self.external_bits.as_slice().iter().all(|&w| w == 0)
}
#[must_use]
pub fn len(&self) -> usize {
usize::from(self.eof)
+ usize::from(self.end_of_nonterminal_extra)

View file

@ -94,6 +94,7 @@ pub struct LexTable {
impl ParseTableEntry {
#[must_use]
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
reusable: true,
@ -103,6 +104,7 @@ impl ParseTableEntry {
}
impl ParseState {
#[must_use]
pub fn is_end_of_non_terminal_extra(&self) -> bool {
self.terminal_entries
.contains_key(&Symbol::end_of_nonterminal_extra())