Skip to content

Commit

Permalink
doc: Add doc comments
Browse files Browse the repository at this point in the history
Signed-off-by: Muhammad Mahad <[email protected]>
  • Loading branch information
MahadMuhammad authored and CohenArthur committed Sep 16, 2024
1 parent 8fe6b59 commit e2532f7
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 5 deletions.
8 changes: 8 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
//! This module contains the command line interface for the tool
use {
anyhow::{Context, Result},
clap::Parser,
std::{fs, path},
};

/// Command line arguments for the tool
#[derive(Parser, Debug)]
#[command(
name = "rust test to DejaGnu",
long_about = "A tool to convert rust tests into DejaGnu tests format"
)]
pub struct Arguments {
/// The rust source file to convert into `DejaGnu` format
#[arg(
short = 'f',
long = "file",
Expand All @@ -18,6 +22,7 @@ pub struct Arguments {
)]
pub source_file: path::PathBuf,

/// optional `stderr` file
#[arg(
short = 'e',
long = "stderr",
Expand All @@ -33,6 +38,7 @@ pub fn parse_arguments_and_read_file(args: &Arguments) -> Result<(String, Option
let source_code = fs::read_to_string(&args.source_file)
.with_context(|| format!("could not read sourcefile `{}`", args.source_file.display()))?;

// Read the stderr file if it exists
let err_file =
match &args.stderr_file {
Some(stderr_file) => Some(fs::read_to_string(stderr_file).with_context(|| {
Expand Down Expand Up @@ -66,6 +72,8 @@ mod tests {
assert_eq!(args.stderr_file, Some(path::PathBuf::from("test.stderr")));
}

/// clap reports most development errors as `debug_assert!`s
/// See this for more details, [here](https://docs.rs/clap/4.5.15/clap/_derive/_tutorial/chapter_4/index.html)
#[test]
fn debug_args() {
use clap::CommandFactory;
Expand Down
25 changes: 24 additions & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! This module contains the logic for parsing rustc error messages.
use {
self::WhichLine::*,
std::{fmt, str::FromStr},
Expand All @@ -11,7 +13,9 @@ macro_rules! regex {
RE.get_or_init(|| regex::Regex::new($re).unwrap())
}};
}
// https://rustc-dev-guide.rust-lang.org/tests/ui.html#error-levels

/// Represents the different kinds of Rustc compiler messages.
/// See [rustc dev guide](https://rustc-dev-guide.rust-lang.org/tests/ui.html#error-levels)
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RustcErrorKind {
Help,
Expand Down Expand Up @@ -55,6 +59,7 @@ impl fmt::Display for RustcErrorKind {
}
}

/// To store information from rustc source file
#[derive(Debug)]
pub struct Error {
pub line_num: usize,
Expand All @@ -67,11 +72,14 @@ pub struct Error {
/// What kind of message we expect (e.g., warning, error, suggestion).
/// `None` if not specified or unknown message kind.
pub kind: Option<RustcErrorKind>,
///Note: if we are loading this from rustc source file, this might be incomplete
pub msg: String,
pub error_code: Option<String>,
}

impl fmt::Display for Error {
/// Formats the `Error` for display according to `DejaGnu` format
/// See `DejaGnu` documentation [here](https://gcc.gnu.org/onlinedocs/gccint/testsuites/directives-used-within-dejagnu-tests/syntax-and-descriptions-of-test-directives.html)
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use RustcErrorKind::*;

Expand Down Expand Up @@ -105,15 +113,20 @@ impl fmt::Display for Error {
}
}

/// Represents the line in the rustc source code where an error occurred.
/// Luckily, rust compile test only stores error messages on and after the line where the error occurred.
/// But `DejaGnu` can process error messages on the previous line, the current line, or the next line.
#[derive(PartialEq, Debug)]
enum WhichLine {
ThisLine,
FollowPrevious(usize),
AdjustBackward(usize),
}

/// The main function for loading errors from source file and from optional stderr file.
pub fn load_error(text_file: &str, stderr_file: Option<&str>) -> Vec<Error> {
let mut last_unfollow_error = None;
// For storing the errors
let mut errors = Vec::new();

for (line_num, line) in text_file.lines().enumerate() {
Expand All @@ -126,12 +139,17 @@ pub fn load_error(text_file: &str, stderr_file: Option<&str>) -> Vec<Error> {
}
}

// If stderr file is not provided, return the errors
if stderr_file.is_none() {
return errors;
}
// TODO: improve this code incrementally
// parsing error related information from `.stderr` file
let error_code_stderr = parse_error_code(stderr_file.expect("stderr file is not found"));

// TODO: We need to load error messages from `.stderr` instead of source file become sometimes source file contains incomplete error messages
// finding the error code w.r.t line number and error message
// TODO: sometimes, the error message might not be same but this doesn't matter as we are not comparing the row number for the message
for error in errors.iter_mut() {
for error_code in error_code_stderr.iter() {
if error.line_num == error_code.line_number
Expand All @@ -141,9 +159,11 @@ pub fn load_error(text_file: &str, stderr_file: Option<&str>) -> Vec<Error> {
}
}
}
// return error detail with error codes
errors
}

/// To represent information from `stderr` file
#[derive(Debug)]
struct StderrResult {
error_code: String,
Expand All @@ -155,6 +175,7 @@ fn is_error_code(s: &str) -> bool {
regex!(r"^E\d{4}$").is_match(s)
}

/// Parses error codes from the `stderr` file
fn parse_error_code(stderr_content: &str) -> Vec<StderrResult> {
// Modified regex pattern with named capture groups
let error_pattern = regex!(
Expand Down Expand Up @@ -191,6 +212,7 @@ fn parse_error_code(stderr_content: &str) -> Vec<StderrResult> {
results
}

/// Parses error details from a source line.
fn parse_expected(
last_nonfollow_error: Option<usize>,
line_num: usize,
Expand Down Expand Up @@ -228,6 +250,7 @@ fn parse_expected(

let msg = msg.trim().to_owned();

// If we find `//~|` or `//~^`, we need to adjust the line number.
let mut relative_line_num = line_num as i32;
let (which, line_num) = if follow {
assert_eq!(adjusts, 0, "use either //~| or //~^, not both.");
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The main entry point of the program.
use {
anyhow::{Context, Result},
clap::Parser,
Expand Down
19 changes: 15 additions & 4 deletions src/transform.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,47 @@
//! This module contains the code transformation logic.
use {
crate::{errors, regex},
anyhow::Result,
};

/// This function takes the rust code as input
/// and returns the code with DejaGnu directive
/// Transform code to `DejaGnu` format
pub fn transform_code(code: &str, stderr_file: Option<&str>) -> Result<String> {
// Load the rustc error messages, codes, lines and relative line numbers
let errors = errors::load_error(code, stderr_file);
// For storing the transformed code
let mut new_code = String::new();

let mut line_num = 1;
// finding the respective line number and adding the error code
for line in code.lines() {
let mut new_line = line.to_string();
// TODO: This is not the efficient way to find respective line number
for error in errors.iter() {
// Checking the original line number
if (error.line_num as i32 - error.relative_line_num) != line_num {
continue;
}
// In rustc test suites, the error directive is
// on the same line or the next line not on the previous line
// on the same line or on the next line, but not on the previous line
// See this: https://rustc-dev-guide.rust-lang.org/tests/ui.html#error-annotations
// For the error on the next line
if error.relative_line_num != 0 {
// We simply add the error message, not to worry about the code
// The error was printed by our overloaded `Display` trait
new_line = format!("{}", error);
} else {
// For the error on the same line
// For the error on the same line, we need to add error message at the end of the line
let captures = regex!(r"//(?:\[(?P<revs>[\w\-,]+)])?~(?P<adjust>\||\^*)")
.captures(line)
.expect("Could not find the error directive");

// Get the part of comment before the sigil (e.g. `~^` or ~|)
let whole_match = captures.get(0).unwrap();
// Get the existing source code before the error directive //~ ERROR or similar to this
let before_match = &line[..whole_match.start()];

// The error was printed by our overloaded `Display` trait
new_line = format!("{}{}", before_match, error);
}
break;
Expand Down

0 comments on commit e2532f7

Please sign in to comment.