Skip to content

Commit 30f6ea1

Browse files
committed
(errors) add: doc
1 parent 329ffbc commit 30f6ea1

File tree

9 files changed

+167
-25
lines changed

9 files changed

+167
-25
lines changed

.github/workflows/rust.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,12 @@ jobs:
4848
- uses: actions/checkout@v4
4949
- name: Run tests
5050
run: cargo test --verbose
51+
52+
doc:
53+
needs: install
54+
runs-on: ubuntu-latest
55+
56+
steps:
57+
- uses: actions/checkout@v4
58+
- name: Check doc
59+
run: cargo doc --document-private-items --all --verbose

src/errors/compile.rs

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,61 @@
1+
use core::fmt;
2+
13
use crate::errors::api::Location;
24

5+
/// Struct to store the error information
6+
///
7+
/// # Creation
8+
///
9+
/// To create an error, you need to have the [`Location`] of the error. Then,
10+
/// use the methods on that location, for example:
11+
///
12+
/// ```ignore
13+
/// let location = Location::from("filename.c");
14+
/// let error = location.to_error("Something bad happened here.".to_owned());
15+
/// ```
16+
///
17+
/// To see the others methods to create errors see [`Location`].
18+
///
19+
/// # Usage
20+
///
21+
/// The [`CompileError`] is mainly used as part of a
22+
/// [`Res`](super::result::Res).
323
#[derive(Debug)]
424
pub struct CompileError {
25+
/// Severity of the error
526
err_lvl: ErrorLevel,
27+
/// Length of the erroneous token or expression
628
length: usize,
29+
/// Location of the error in the C source file
730
location: Location,
31+
/// Error message to be displayed to the user
832
message: String,
933
}
1034

1135
impl CompileError {
12-
pub fn get(self) -> (Location, String, &'static str, usize) {
36+
/// Returns the owned data of a `CompileError`.
37+
pub(super) fn into_values(self) -> (Location, String, String, usize) {
1338
(
1439
self.location,
1540
self.message,
16-
self.err_lvl.repr(),
41+
self.err_lvl.to_string(),
1742
self.length,
1843
)
1944
}
2045

21-
pub fn is_error(&self) -> bool {
46+
/// Checks if the error is of severity [`ErrorLevel::Error`].
47+
pub(crate) fn is_error(&self) -> bool {
2248
self.err_lvl == ErrorLevel::Error
2349
}
2450

25-
pub fn specify_length(&mut self, length: usize) {
51+
// Replaces length of the token or expression concerned by the `CompileError`.
52+
pub(crate) fn specify_length(&mut self, length: usize) {
2653
self.length = length;
2754
}
2855
}
2956

3057
impl From<(Location, String, ErrorLevel, usize)> for CompileError {
58+
#[inline]
3159
fn from((location, message, err_lvl, length): (Location, String, ErrorLevel, usize)) -> Self {
3260
Self {
3361
err_lvl,
@@ -39,6 +67,7 @@ impl From<(Location, String, ErrorLevel, usize)> for CompileError {
3967
}
4068

4169
impl From<(Location, String, ErrorLevel)> for CompileError {
70+
#[inline]
4271
fn from((location, message, err_lvl): (Location, String, ErrorLevel)) -> Self {
4372
Self {
4473
message,
@@ -49,19 +78,44 @@ impl From<(Location, String, ErrorLevel)> for CompileError {
4978
}
5079
}
5180

81+
/// Different levels of errors
5282
#[derive(Debug, PartialEq, Eq)]
5383
pub enum ErrorLevel {
84+
/// The compiler stops compiling the current block and fails.
85+
///
86+
/// The level is only `Error` when the compiler can't fix the error and
87+
/// panics.
88+
///
89+
/// The compiler will continue if it manages to do so safely on parts that
90+
/// are independent from the original location of the error. Not all of the
91+
/// independent parts are compiled though.
5492
Error,
93+
/// Found a bad practice.
94+
///
95+
/// # Examples
96+
///
97+
/// - a leading space after `\` at end of line
5598
Suggestion,
99+
/// The compiler manages to fix the code and continue.
100+
///
101+
/// A warning is displayed to the user, but the compiler continues as
102+
/// nothing happened.
103+
///
104+
/// # Examples
105+
///
106+
/// - an overflow on a integer constant: the value is crapped and the
107+
/// compiler continues
108+
/// - deprecated behaviours (e.g. using `_Bool` instead of `bool` in C23).
56109
Warning,
57110
}
58111

59-
impl ErrorLevel {
60-
const fn repr(&self) -> &'static str {
112+
#[expect(clippy::min_ident_chars)]
113+
impl fmt::Display for ErrorLevel {
114+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61115
match self {
62-
Self::Warning => "warning",
63-
Self::Error => "error",
64-
Self::Suggestion => "suggestion",
116+
Self::Error => "error".fmt(f),
117+
Self::Suggestion => "suggestion".fmt(f),
118+
Self::Warning => "warning".fmt(f),
65119
}
66120
}
67121
}

src/errors/display.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ use std::collections::HashMap;
33

44
use super::compile::CompileError;
55

6-
#[inline]
7-
pub fn display_errors(
6+
/// Transforms [`CompileError`] into a human-readable string
7+
///
8+
/// See [`Res::get_displayed_errors`](super::result::Res::get_displayed_errors)
9+
/// for extra information and examples.
10+
pub(super) fn display_errors(
811
errors: Vec<CompileError>,
912
files: &[(String, &str)],
1013
err_type: &str,
@@ -15,7 +18,7 @@ pub fn display_errors(
1518
files_status.insert(filename.to_owned(), content.lines().collect());
1619
}
1720
for error in errors {
18-
let (location, message, err_lvl, length) = error.get();
21+
let (location, message, err_lvl, length) = error.into_values();
1922
let (filename, line_nb, column_nb) = location.into_values();
2023
let code_lines = files_status
2124
.get(&filename)

src/errors/location.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@ use core::fmt;
22

33
use super::compile::{CompileError, ErrorLevel};
44

5+
/// Struct to pinpoint a precise character in the C source file.
6+
///
7+
/// The locations are computed by the lexer that reads the C source file. Then,
8+
/// the locations are stored inside the tokens to keep them for the rest of the
9+
/// compiler.
10+
///
11+
/// # Note
12+
///
13+
/// In order to respect the click links from terminals, the line and column of
14+
/// a file start at 1 and not 0.
515
#[derive(Debug, Clone)]
616
pub struct Location {
717
col: usize,
@@ -10,38 +20,53 @@ pub struct Location {
1020
}
1121

1222
impl Location {
13-
pub(crate) const fn get_values(&self) -> (&String, usize, usize) {
14-
(&self.file, self.line, self.col)
15-
}
16-
23+
/// Increments column of location by 1
24+
///
25+
/// This is used by lexer when parsing every character of the C file.
1726
pub(crate) fn incr_col(&mut self) {
1827
self.col += 1;
1928
}
2029

30+
/// Increments line of location by 1
31+
///
32+
/// This is used by lexer when parsing every line of the C file.
2133
pub(crate) fn incr_line(&mut self) {
2234
self.line += 1;
2335
self.col = 1;
2436
}
2537

38+
/// Creates an error from a location without cloning
2639
pub(crate) fn into_error(self, msg: String) -> CompileError {
2740
CompileError::from((self, msg, ErrorLevel::Error))
2841
}
2942

43+
/// Moves the location back a few character on the current line
44+
///
45+
/// If the offset is too big, the column is set to minimal (1) without any
46+
/// warnings or errors.
3047
pub(crate) fn into_past(self, offset: usize) -> Self {
3148
Self {
3249
col: self.col.checked_sub(offset).unwrap_or(1),
3350
..self
3451
}
3552
}
3653

54+
/// Returns the owned data of a `Location`.
3755
pub(crate) fn into_values(self) -> (String, usize, usize) {
3856
(self.file, self.line, self.col)
3957
}
4058

59+
/// Creates an error by cloning the location.
4160
pub(crate) fn to_error(&self, msg: String) -> CompileError {
4261
CompileError::from((self.to_owned(), msg, ErrorLevel::Error))
4362
}
4463

64+
/// Returns a clone of the current file name.
65+
pub(crate) fn to_filename(&self) -> String {
66+
self.file.clone()
67+
}
68+
69+
/// Creates an warning by cloning the location.
4570
pub(crate) fn to_suggestion(&self, msg: String) -> CompileError {
4671
CompileError::from((self.to_owned(), msg, ErrorLevel::Warning))
4772
}

src/errors/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
//! Module to deal with compiler errors
2+
//!
3+
//! This module provides the tools to store the information on errors during
4+
//! compile-time and display these errors to the user at the end of the
5+
//! compilation process.
6+
17
pub mod api {
28
#![allow(clippy::pub_use)]
39

src/errors/result.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,64 @@ use super::display::display_errors;
55

66
type PublicRes<T> = (T, Vec<CompileError>);
77

8+
/// Struct to store the errors, whilst still having the desired value.
9+
///
10+
/// This struct is meant as a [`Result`], but with the were it is possible to
11+
/// have a value and some errors at the same time. It is for example the case
12+
/// for warnings and suggestions (see
13+
/// [`CompileError`] for more information), that must be stored, and at the
14+
/// same time, the compiler continues to work.
815
#[derive(Debug)]
916
pub struct Res<T> {
1017
errors: Vec<CompileError>,
1118
result: T,
1219
}
1320

1421
impl<T> Res<T> {
22+
/// Returns all the errors in a user-readable format.
23+
///
24+
/// # Returns
25+
///
26+
/// A [`String`] containing all the errors, displayed in a user-readable
27+
/// format, with a clickable location, and an explanation message.
28+
///
29+
/// # Examples
30+
///
31+
/// ```
32+
/// use std::fs;
33+
///
34+
/// use c_parser::{lex_file, Location};
35+
///
36+
/// let content = "int m@in() { }";
37+
/// let res = lex_file(&content, &mut Location::from("filename.c"));
38+
/// let errors = res.get_displayed_errors(&[("filename.c".to_owned(), content)], "lexer");
39+
/// let expected =
40+
/// "filename.c:1:6: lexer error: Character '@' not supported in context of a 'identifier'.
41+
/// 1 | int m@in() { }
42+
/// ^
43+
/// ";
44+
///
45+
/// assert!(errors == expected, "!{errors}!\n!{expected}!");
46+
/// ```
47+
///
48+
/// # Panics
49+
///
50+
/// If there is at least one error of level `Error`.
1551
#[inline]
1652
pub fn get_displayed_errors(self, files: &[(String, &str)], err_type: &str) -> String {
1753
display_errors(self.errors, files, err_type)
18-
.expect("Buffer overflow, failed to fecth errors")
54+
.expect("Buffer overflow, failed to fetch errors")
1955
}
2056

57+
/// Prints all the errors to the user.
58+
///
59+
/// # Returns
60+
///
61+
/// The value of the [`Res`] if there aren't any errors of level `Error`.
62+
///
63+
/// # Panics
64+
///
65+
/// If there is at least one error of level `Error`.
2166
#[inline]
2267
#[expect(clippy::print_stderr)]
2368
pub fn unwrap_or_display(self, files: &[(String, &str)], err_type: &str) -> T {
@@ -31,13 +76,15 @@ impl<T> Res<T> {
3176
}
3277

3378
impl<T: Default> Res<T> {
79+
/// Creates a [`Res`] from one error
3480
pub(crate) fn from_err(err: CompileError) -> Self {
3581
Self {
3682
result: T::default(),
3783
errors: vec![err],
3884
}
3985
}
4086

87+
/// Creates a [`Res`] from a list of errors
4188
pub(crate) fn from_errors(errors: Vec<CompileError>) -> Self {
4289
Self {
4390
result: T::default(),

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ mod lexer;
5959
mod parser;
6060

6161
#[expect(clippy::useless_attribute, clippy::pub_use)]
62-
pub use crate::errors::api::{Location, Res};
62+
pub use crate::errors::api::{CompileError, Location, Res};
6363
#[expect(clippy::useless_attribute, clippy::pub_use)]
6464
pub use crate::lexer::api::{display_tokens, lex_file, Number, TokenValue};
6565
#[expect(clippy::useless_attribute, clippy::pub_use)]

src/parser/parse_content.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ pub fn parse_block(
9292
#[inline]
9393
pub fn parse_tokens(tokens: Vec<Token>) -> Res<Ast> {
9494
let mut nodes = vec![];
95-
let filename = tokens
96-
.first()
97-
.map(|node| node.get_location().get_values().0.to_owned());
95+
let filename = tokens.first().map(|node| node.get_location().to_filename());
9896
let mut tokens_iter = tokens.into_iter();
9997
while tokens_iter.len() != 0 {
10098
let mut outer_node_block = Ast::Block(Block::default());

src/parser/tree/node.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ impl Ast {
211211
}
212212
}
213213

214-
/// Adds the colon of a [`super::TernaryOperator`].
214+
/// Adds the colon of a [`TernaryOperator`](super::TernaryOperator).
215215
///
216216
/// This method finds a ternary operator, and changes its reading state to
217217
/// failure.
@@ -266,9 +266,9 @@ impl Ast {
266266

267267
/// Make an [`Ast`] a LHS node
268268
///
269-
/// This is called when an assign [`super::Operator`] is created or a
270-
/// function is created, to convert `*` to a type attribute. It also
271-
/// check that the [`Ast`] is a valid LHS.
269+
/// This is called when an assign [`Operator`](super::Operator) is created
270+
/// or a function is created, to convert `*` to a type attribute. It
271+
/// also check that the [`Ast`] is a valid LHS.
272272
pub fn make_lhs(&mut self) -> Result<(), String> {
273273
self.make_lhs_aux(false)
274274
}

0 commit comments

Comments
 (0)