-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add rust edition
feature
#12
base: main
Are you sure you want to change the base?
Changes from all commits
06cbf37
54a42df
a645a19
f1b8490
86cf8ae
8bd6afe
fb038fc
0a53af7
2a84c71
81c4991
70bf66c
e180b3b
dec1beb
d71ddfe
15d5640
e561a01
8a33157
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,10 +1,21 @@ | ||||||||||||||||||||||||||||||
use std::{cell::OnceCell, fmt, str::FromStr}; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
use regex::Regex; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
use self::WhichLine::*; | ||||||||||||||||||||||||||||||
//! This module contains the logic for parsing rustc error messages. | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
use { | ||||||||||||||||||||||||||||||
self::WhichLine::*, | ||||||||||||||||||||||||||||||
std::{fmt, str::FromStr}, | ||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
// https://docs.rs/once_cell/1.19.0/once_cell/#lazily-compiled-regex | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Macros can be tricky to understand, I'd like this to be more thoroughly documented. A doctest example even provides a great insight (the example should compile & run when you launch cargo test). |
||||||||||||||||||||||||||||||
#[macro_export] | ||||||||||||||||||||||||||||||
macro_rules! regex { | ||||||||||||||||||||||||||||||
($re:literal $(,)?) => {{ | ||||||||||||||||||||||||||||||
static RE: once_cell::sync::OnceCell<regex::Regex> = once_cell::sync::OnceCell::new(); | ||||||||||||||||||||||||||||||
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, | ||||||||||||||||||||||||||||||
|
@@ -48,6 +59,7 @@ impl fmt::Display for RustcErrorKind { | |||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
/// To store information from rustc source file | ||||||||||||||||||||||||||||||
#[derive(Debug)] | ||||||||||||||||||||||||||||||
pub struct Error { | ||||||||||||||||||||||||||||||
pub line_num: usize, | ||||||||||||||||||||||||||||||
|
@@ -60,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 | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
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) | ||||||||||||||||||||||||||||||
Comment on lines
+81
to
+82
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||||||||||||||||||||||||||
use RustcErrorKind::*; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let error_type = match &self.kind {
Some(Help) => "help",
Some(Note) => "dg-note",
Some(Suggestion) => "suggestion",
Some(Warning) => "dg-warning",
Some(Error) | None => "dg-error",
}; |
||||||||||||||||||||||||||||||
|
@@ -98,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() { | ||||||||||||||||||||||||||||||
|
@@ -119,24 +139,31 @@ 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 | ||||||||||||||||||||||||||||||
&& error.msg == error_code.error_message_detail | ||||||||||||||||||||||||||||||
|| error.msg == error_code.error_message_detail | ||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||
error.error_code = Some(error_code.error_code.clone()); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
// return error detail with error codes | ||||||||||||||||||||||||||||||
errors | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
/// To represent information from `stderr` file | ||||||||||||||||||||||||||||||
#[derive(Debug)] | ||||||||||||||||||||||||||||||
struct StderrResult { | ||||||||||||||||||||||||||||||
error_code: String, | ||||||||||||||||||||||||||||||
|
@@ -145,17 +172,15 @@ struct StderrResult { | |||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
fn is_error_code(s: &str) -> bool { | ||||||||||||||||||||||||||||||
let re: OnceCell<Regex> = OnceCell::new(); | ||||||||||||||||||||||||||||||
let regex = re.get_or_init(|| Regex::new(r"^E\d{4}$").unwrap()); | ||||||||||||||||||||||||||||||
regex.is_match(s) | ||||||||||||||||||||||||||||||
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 re: OnceCell<Regex> = OnceCell::new(); | ||||||||||||||||||||||||||||||
let error_pattern = re.get_or_init(|| { | ||||||||||||||||||||||||||||||
Regex::new(r"error\[(?P<error_code>E\d{4})\]: (?P<error_message_detail>.+?)\n\s+-->.+:(?P<line_number>\d+):").unwrap() | ||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||
let error_pattern = regex!( | ||||||||||||||||||||||||||||||
r"error\[(?P<error_code>E\d{4})\]: (?P<error_message_detail>.+?)\n\s+-->.+:(?P<line_number>\d+):" | ||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
let mut results = Vec::new(); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
@@ -187,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, | ||||||||||||||||||||||||||||||
|
@@ -197,11 +223,8 @@ fn parse_expected( | |||||||||||||||||||||||||||||
// //~| | ||||||||||||||||||||||||||||||
// //~^ | ||||||||||||||||||||||||||||||
// //~^^^^^ | ||||||||||||||||||||||||||||||
let re: OnceCell<Regex> = OnceCell::new(); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
let captures = re | ||||||||||||||||||||||||||||||
.get_or_init(|| Regex::new(r"//(?:\[(?P<revs>[\w\-,]+)])?~(?P<adjust>\||\^*)").unwrap()) | ||||||||||||||||||||||||||||||
.captures(line)?; | ||||||||||||||||||||||||||||||
let captures = regex!(r"//(?:\[(?P<revs>[\w\-,]+)])?~(?P<adjust>\||\^*)").captures(line)?; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
let (follow, adjusts) = match &captures["adjust"] { | ||||||||||||||||||||||||||||||
"|" => (true, 0), | ||||||||||||||||||||||||||||||
|
@@ -227,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."); | ||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
//! This module contains the logic for parsing rust test headers | ||
//! See [rustc dev guide](https://rustc-dev-guide.rust-lang.org/tests/headers.html#test-headers) | ||
|
||
#[derive(Debug)] | ||
pub struct HeaderLine<'ln> { | ||
pub line_number: usize, | ||
/// The main part of the header directive, after removing the comment prefix | ||
/// and the optional revision specifier. | ||
pub _directive: &'ln str, | ||
/// DejaGnu formatted header line | ||
pub dejagnu_header: String, | ||
} | ||
|
||
pub fn parse_additional_options(code: &str) -> Vec<HeaderLine> { | ||
let mut headers = Vec::new(); | ||
|
||
for (line_number, line) in code.lines().enumerate() { | ||
let line = line.trim(); | ||
if line.is_empty() || line.starts_with("fn") || line.starts_with("mod") { | ||
continue; | ||
} | ||
if is_header_line(line) { | ||
if let Some(header_info) = add_additional_options(line, line_number) { | ||
headers.push(header_info); | ||
} | ||
} | ||
} | ||
headers | ||
} | ||
|
||
pub fn is_header_line(line: &str) -> bool { | ||
line.trim_start().starts_with("//@") | ||
} | ||
|
||
fn add_additional_options(code: &str, line_number: usize) -> Option<HeaderLine> { | ||
//TODO: If we know the file extension, then update this to | ||
// let comment = if testfile.extension().is_some_and(|e| e == "rs") { "//@" } else { "#" }; | ||
let comment = "//@"; | ||
|
||
if let Some((_header_revision, non_revisioned_directive_line)) = line_directive(comment, code) { | ||
// The non_revisioned_directive_line is the directive without the "//@" prefix | ||
let edition = parse_edition(non_revisioned_directive_line); | ||
edition.as_ref()?; | ||
Some(HeaderLine { | ||
line_number: line_number + 1, // 1 based-indexed instead of zero based | ||
_directive: "edition", | ||
dejagnu_header: to_dejagnu_edition(edition.unwrap().as_str()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why can we unwrap edition safely without panicking here ? |
||
}) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn line_directive<'line>( | ||
comment: &str, | ||
original_line: &'line str, | ||
) -> Option<(Option<&'line str>, &'line str)> { | ||
let after_comment = original_line | ||
.trim_start() | ||
.strip_prefix(comment)? | ||
.trim_start(); | ||
|
||
if let Some(after_open_bracket) = after_comment.strip_prefix('[') { | ||
let Some((line_revision, directive)) = after_open_bracket.split_once(']') else { | ||
panic!( | ||
"malformed condition directive: expected `{comment}[foo]`, found `{original_line}`" | ||
) | ||
Comment on lines
+65
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not thrilled about this panic here |
||
}; | ||
|
||
Some((Some(line_revision), directive.trim_start())) | ||
} else { | ||
Some((None, after_comment)) | ||
} | ||
} | ||
|
||
fn parse_edition(line: &str) -> Option<String> { | ||
parse_name_value_directive(line, "edition") | ||
} | ||
|
||
fn parse_name_value_directive(line: &str, directive: &str) -> Option<String> { | ||
let colon = directive.len(); | ||
|
||
if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { | ||
let value = line[(colon + 1)..].to_owned(); | ||
Some(value) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn to_dejagnu_edition(edition: &str) -> String { | ||
format!( | ||
"// {{ dg-additional-options \"-frust-edition={}\" }}", | ||
edition | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure a doc comment is required ? A regular comment might be enough