Repository: swc-project/swc
File: crates/swc_common/src/input.rs (Rust)

Code before:
        debug_assert!(start <= end, "Cannot slice {start:?}..{end:?}");
        let s = self.orig;

        let start_idx = (start - self.orig_start).0 as usize;
        let end_idx = (end - self.orig_start).0 as usize;

        debug_assert!(end_idx <= s.len());

        let ret = unsafe { s.get_unchecked(start_idx..end_idx) };

        self.iter = unsafe { s.get_unchecked(end_idx..) }.chars();

        ret
    }

    #[inline]
    pub fn bump_bytes(&mut self, n: usize) {
        unsafe {
            // Safety: We only proceed, not go back.
            self.reset_to(self.last_pos + BytePos(n as u32));
        }
    }

    pub fn start_pos(&self) -> BytePos {
        self.orig_start
    }

    #[inline(always)]
    pub fn end_pos(&self) -> BytePos {
        self.orig_end
    }
}

/// Creates an [Input] from [SourceFile]. This is an alias for
///
/// ```ignore
///    StringInput::new(&fm.src, fm.start_pos, fm.end_pos)
/// ```
impl<'a> From<&'a SourceFile> for StringInput<'a> {
    fn from(fm: &'a SourceFile) -> Self {
        StringInput::new(&fm.src, fm.start_pos, fm.end_pos)
    }
}

impl<'a> Input<'a> for StringInput<'a> {
    #[inline]
    fn cur(&self) -> Option<char> {
        self.iter.clone().next()
    }

    #[inline]

Review comment (bug):
The `bump_one` method unconditionally adds 1 to `last_pos`, but this is only correct for single-byte ASCII characters. Multi-byte UTF-8 characters will cause position tracking to become incorrect. The method should use `c.len_utf8()` like the `bump()` method does (line 141), or be documented to only work with ASCII characters and add a debug assertion to enforce this.
```suggestion
        if let Some(c) = self.iter.next() {
            self.last_pos.0 += c.len_utf8() as u32;
```

Code after:
        debug_assert!(start <= end, "Cannot slice {start:?}..{end:?}");
        let s = self.orig;

        let start_idx = (start - self.orig_start).0 as usize;
        let end_idx = (end - self.orig_start).0 as usize;

        debug_assert!(end_idx <= s.len());

        let ret = unsafe { s.get_unchecked(start_idx..end_idx) };

        self.iter = unsafe { s.get_unchecked(end_idx..) }.chars();

        ret
    }

    #[inline]
    pub fn bump_bytes(&mut self, n: usize) {
        let s = self.iter.as_str();
        self.iter = unsafe { s.get_unchecked(n..) }.chars();
        self.last_pos.0 += n as u32;
    }

    #[inline]
    pub fn bump_one(&mut self) {
        if self.iter.next().is_some() {
            self.last_pos.0 += 1;
        } else {
            unsafe {
                debug_unreachable!("bump should not be called when cur() == None");
            }
        }
    }

    pub fn start_pos(&self) -> BytePos {
        self.orig_start
    }

    #[inline(always)]
    pub fn end_pos(&self) -> BytePos {
        self.orig_end
    }
}

/// Creates an [Input] from [SourceFile]. This is an alias for
///
/// ```ignore
///    StringInput::new(&fm.src, fm.start_pos, fm.end_pos)
/// ```
impl<'a> From<&'a SourceFile> for StringInput<'a> {
    fn from(fm: &'a SourceFile) -> Self {
        StringInput::new(&fm.src, fm.start_pos, fm.end_pos)