From 7ccefac5530e287a9563125943021688762af532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kha=C3=AFs=20COLIN=20=28aider=29?= Date: Thu, 5 Jun 2025 10:18:26 +0200 Subject: [PATCH] docs: Add comprehensive documentation to src/tokens/error.rs --- .../doctest_tokens_rs__invalid integer.snap | 20 +++ ...ctest_tokens_rs__invalid meta-command.snap | 18 +++ ...test_tokens_rs__unclosed double quote.snap | 23 +++ src/tokens/error.rs | 147 ++++++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 src/snapshots/doctest_tokens_rs__invalid integer.snap create mode 100644 src/snapshots/doctest_tokens_rs__invalid meta-command.snap create mode 100644 src/snapshots/doctest_tokens_rs__unclosed double quote.snap diff --git a/src/snapshots/doctest_tokens_rs__invalid integer.snap b/src/snapshots/doctest_tokens_rs__invalid integer.snap new file mode 100644 index 0000000..846d27d --- /dev/null +++ b/src/snapshots/doctest_tokens_rs__invalid integer.snap @@ -0,0 +1,20 @@ +--- +source: src/tokens.rs +expression: err +--- +Err( + [ + ScanError { + location: Location { + file: "", + offset: 7, + length: 19, + }, + kind: ParseIntError( + ParseIntError { + kind: PosOverflow, + }, + ), + }, + ], +) diff --git a/src/snapshots/doctest_tokens_rs__invalid meta-command.snap b/src/snapshots/doctest_tokens_rs__invalid meta-command.snap new file mode 100644 index 0000000..5a4142f --- /dev/null +++ b/src/snapshots/doctest_tokens_rs__invalid meta-command.snap @@ -0,0 +1,18 @@ +--- +source: src/tokens.rs +expression: err +--- +Err( + [ + ScanError { + location: Location { + file: "", + offset: 0, + length: 8, + }, + kind: UnknownMetaCommand( + ".invalid", + ), + }, + ], +) diff --git a/src/snapshots/doctest_tokens_rs__unclosed double quote.snap b/src/snapshots/doctest_tokens_rs__unclosed double quote.snap new file mode 100644 index 0000000..87bce2d --- /dev/null +++ b/src/snapshots/doctest_tokens_rs__unclosed double quote.snap @@ -0,0 +1,23 @@ +--- +source: src/tokens.rs +expression: err +--- +Err( + [ + ScanError { + location: Location { + file: "", + offset: 18, + length: 0, + }, + kind: UnexpectedEndOfInputWhileLookingForMatching( + '"', + Location { + file: "", + offset: 9, + length: 1, + }, + ), + }, + ], +) diff --git a/src/tokens/error.rs b/src/tokens/error.rs index c71df1a..1788c4e 100644 --- a/src/tokens/error.rs +++ b/src/tokens/error.rs @@ -1,12 +1,136 @@ +//! Lexical analysis error and diagnostics +//! +//! This module contains types for representing errors that occur during +//! the tokenization phase of parsing. These errors capture both the type +//! of lexical issue and its precise location in source code. +//! +//! # Example +//! ``` +//! use osdb::tokens::error::ScanError; +//! use osdb::tokens::error::ScanErrorKind; +//! use osdb::tokens::location::Location; +//! +//! // Creating a scan error for an unexpected character +//! let location = Location::new("query.sql".into(), 10, 1); +//! let error = ScanError { +//! location, +//! kind: ScanErrorKind::UnexpectedChar('$'), +//! }; +//! assert_eq!(format!("{}", error), "unexpected char: '$'"); +//! ``` + use super::Location; +/// Category of lexical error encountered during tokenization +/// +/// Represents different types of errors that can occur when scanning source text +/// into tokens, each carrying relevant context about the specific error condition. #[derive(Debug, Eq, PartialEq)] pub enum ScanErrorKind { + /// Unexpected character in input stream + /// + /// Occurs when encountering a character that is invalid in the current scanning context. + /// + /// # Example + /// ``` + /// # use osdb::tokens::error::{ScanError, ScanErrorKind}; + /// # use osdb::tokens::location::Location; + /// let error = ScanError { + /// location: Location::new("file.txt".into(), 5, 1), + /// kind: ScanErrorKind::UnexpectedChar('@'), + /// }; + /// assert_eq!(format!("{}", error), "unexpected char: '@'"); + /// ``` UnexpectedChar(char), + + /// Premature end of input + /// + /// Triggered when input ends unexpectedly during scanning of a token or literal. + /// + /// # Example + /// ``` + /// # use osdb::tokens::error::{ScanError, ScanErrorKind}; + /// # use osdb::tokens::location::Location; + /// let error = ScanError { + /// location: Location::new("input".into(), 3, 0), + /// kind: ScanErrorKind::UnexpectedEndOfInput, + /// }; + /// assert_eq!(format!("{}", error), "unexpected end of input"); + /// ``` UnexpectedEndOfInput, + + /// Unrecognized keyword in input + /// + /// Occurs when scanning an identifier that doesn't match any known SQL keyword. + /// + /// # Example + /// ``` + /// # use osdb::tokens::error::{ScanError, ScanErrorKind}; + /// # use osdb::tokens::location::Location; + /// let error = ScanError { + /// location: Location::new("query.sql".into(), 8, 6), + /// kind: ScanErrorKind::UnknownKeyword("SELECTT".into()), + /// }; + /// assert_eq!(format!("{}", error), "unknown keyword: \"SELECTT\""); + /// ``` UnknownKeyword(String), + + /// Invalid meta-command syntax + /// + /// Triggered by unrecognized commands starting with '.' that don't match any known meta-commands. + /// + /// # Example + /// ``` + /// # use osdb::tokens::error::{ScanError, ScanErrorKind}; + /// # use osdb::tokens::location::Location; + /// let error = ScanError { + /// location: Location::new("cmd".into(), 1, 5), + /// kind: ScanErrorKind::UnknownMetaCommand(".test".into()), + /// }; + /// assert_eq!(format!("{}", error), "unknown meta-command: \".test\""); + /// ``` UnknownMetaCommand(String), + + /// Invalid integer literal format + /// + /// Contains the underlying parse error from failed integer conversion. + /// + /// # Example + /// ``` + /// # use osdb::tokens::error::{ScanError, ScanErrorKind}; + /// # use osdb::tokens::location::Location; + /// # use std::num::ParseIntError; + /// let parse_error = "123a".parse::().unwrap_err(); + /// let error = ScanError { + /// location: Location::new("nums".into(), 0, 4), + /// kind: ScanErrorKind::ParseIntError(parse_error), + /// }; + /// assert!(match &error.kind { + /// ScanErrorKind::ParseIntError(_) => true, + /// _ => false + /// }); + /// ``` ParseIntError(std::num::ParseIntError), + + /// Unclosed string literal + /// + /// Occurs when a string literal is missing its closing quote. Carries the quote + /// character and location where the string started. + /// + /// # Example + /// ``` + /// # use osdb::tokens::error::{ScanError, ScanErrorKind}; + /// # use osdb::tokens::location::Location; + /// let start_loc = Location::new("strings".into(), 0, 1); + /// let error = ScanError { + /// location: Location::new("strings".into(), 5, 0), + /// kind: ScanErrorKind::UnexpectedEndOfInputWhileLookingForMatching('"', start_loc), + /// }; + /// assert_eq!( + /// format!("{}", error), + /// "unexpected end of input while looking for matching '\"'" + /// ); + /// ``` UnexpectedEndOfInputWhileLookingForMatching(char, Location), } @@ -26,9 +150,32 @@ impl std::fmt::Display for ScanErrorKind { } } +/// Lexical error with source location information +/// +/// Combines a [`ScanErrorKind`] with precise source code location details +/// to enable rich error reporting and diagnostics. +/// +/// # Example +/// ``` +/// use osdb::tokens::error::ScanError; +/// use osdb::tokens::error::ScanErrorKind; +/// use osdb::tokens::location::Location; +/// +/// let location = Location::new("script.osql".into(), 20, 3); +/// let error = ScanError { +/// location, +/// kind: ScanErrorKind::UnexpectedChar('?'), +/// }; +/// +/// assert_eq!(error.location.file, "script.osql"); +/// assert_eq!(error.location.offset, 20); +/// assert_eq!(format!("{}", error), "unexpected char: '?'"); +/// ``` #[derive(Debug, Eq, PartialEq)] pub struct ScanError { + /// Source location where the error occurred pub location: Location, + /// Specific type of lexical error pub kind: ScanErrorKind, }