diff --git a/apps/oxlint/src/output_formatter/checkstyle.rs b/apps/oxlint/src/output_formatter/checkstyle.rs
index 05655fb3dfad4..adcbd4eed824a 100644
--- a/apps/oxlint/src/output_formatter/checkstyle.rs
+++ b/apps/oxlint/src/output_formatter/checkstyle.rs
@@ -51,14 +51,14 @@ fn format_checkstyle(diagnostics: &[Error]) -> String {
let messages = infos
.iter()
.fold(String::new(), |mut acc, info| {
- let Info { line, column, message, severity, rule_id, .. } = info;
+ let Info { start, message, severity, rule_id, .. } = info;
let severity = match severity {
Severity::Error => "error",
_ => "warning",
};
let message = rule_id.as_ref().map_or_else(|| xml_escape(message), |rule_id| Cow::Owned(format!("{} ({rule_id})", xml_escape(message))));
let source = rule_id.as_ref().map_or_else(|| Cow::Borrowed(""), |rule_id| Cow::Owned(format!("eslint.rules.{rule_id}")));
- let line = format!(r#""#);
+ let line = format!(r#""#, start.line, start.column);
acc.push_str(&line);
acc
});
diff --git a/apps/oxlint/src/output_formatter/github.rs b/apps/oxlint/src/output_formatter/github.rs
index d6b9a33481835..f5739366e0641 100644
--- a/apps/oxlint/src/output_formatter/github.rs
+++ b/apps/oxlint/src/output_formatter/github.rs
@@ -35,7 +35,7 @@ impl DiagnosticReporter for GithubReporter {
}
fn format_github(diagnostic: &Error) -> String {
- let Info { line, column, filename, message, severity, rule_id } = Info::new(diagnostic);
+ let Info { start, end, filename, message, severity, rule_id } = Info::new(diagnostic);
let severity = match severity {
Severity::Error => "error",
Severity::Warning | miette::Severity::Advice => "warning",
@@ -44,7 +44,11 @@ fn format_github(diagnostic: &Error) -> String {
let filename = escape_property(&filename);
let message = escape_data(&message);
format!(
- "::{severity} file={filename},line={line},endLine={line},col={column},endColumn={column},title={title}::{message}\n"
+ "::{severity} file={filename},line={},endLine={},col={},endColumn={},title={title}::{message}\n",
+ start.line,
+ end.line,
+ start.column,
+ end.column
)
}
@@ -108,6 +112,6 @@ mod test {
let result = reporter.render_error(error);
assert!(result.is_some());
- assert_eq!(result.unwrap(), "::warning file=file%3A//test.ts,line=1,endLine=1,col=1,endColumn=1,title=oxlint::error message\n");
+ assert_eq!(result.unwrap(), "::warning file=file%3A//test.ts,line=1,endLine=1,col=1,endColumn=9,title=oxlint::error message\n");
}
}
diff --git a/apps/oxlint/src/output_formatter/stylish.rs b/apps/oxlint/src/output_formatter/stylish.rs
index e6669d61ed82a..7f9e4e2e17bb9 100644
--- a/apps/oxlint/src/output_formatter/stylish.rs
+++ b/apps/oxlint/src/output_formatter/stylish.rs
@@ -49,7 +49,7 @@ fn format_stylish(diagnostics: &[Error]) -> String {
let mut grouped: FxHashMap> = FxHashMap::default();
let mut sorted = diagnostics.iter().collect::>();
- sorted.sort_by_key(|diagnostic| Info::new(diagnostic).line);
+ sorted.sort_by_key(|diagnostic| Info::new(diagnostic).start.line);
for diagnostic in sorted {
let info = Info::new(diagnostic);
diff --git a/apps/oxlint/src/output_formatter/unix.rs b/apps/oxlint/src/output_formatter/unix.rs
index 4c34f334ebd4d..12cfa99699602 100644
--- a/apps/oxlint/src/output_formatter/unix.rs
+++ b/apps/oxlint/src/output_formatter/unix.rs
@@ -45,14 +45,14 @@ impl DiagnosticReporter for UnixReporter {
///
fn format_unix(diagnostic: &Error) -> String {
- let Info { line, column, filename, message, severity, rule_id } = Info::new(diagnostic);
+ let Info { start, end: _, filename, message, severity, rule_id } = Info::new(diagnostic);
let severity = match severity {
Severity::Error => "Error",
_ => "Warning",
};
let rule_id =
rule_id.map_or_else(|| Cow::Borrowed(""), |rule_id| Cow::Owned(format!("/{rule_id}")));
- format!("{filename}:{line}:{column}: {message} [{severity}{rule_id}]\n")
+ format!("{filename}:{}:{}: {message} [{severity}{rule_id}]\n", start.line, start.column)
}
#[cfg(test)]
diff --git a/crates/oxc_diagnostics/src/reporter.rs b/crates/oxc_diagnostics/src/reporter.rs
index 34443c273a934..ffe44986c4586 100644
--- a/crates/oxc_diagnostics/src/reporter.rs
+++ b/crates/oxc_diagnostics/src/reporter.rs
@@ -1,5 +1,7 @@
//! [Reporters](DiagnosticReporter) for rendering and writing diagnostics.
+use miette::SourceSpan;
+
use crate::{Error, Severity};
/// Reporters are responsible for rendering diagnostics to some format and writing them to some
@@ -54,18 +56,23 @@ pub trait DiagnosticReporter {
}
pub struct Info {
- pub line: usize,
- pub column: usize,
+ pub start: InfoPosition,
+ pub end: InfoPosition,
pub filename: String,
pub message: String,
pub severity: Severity,
pub rule_id: Option,
}
+pub struct InfoPosition {
+ pub line: usize,
+ pub column: usize,
+}
+
impl Info {
pub fn new(diagnostic: &Error) -> Self {
- let mut line = 0;
- let mut column = 0;
+ let mut start = InfoPosition { line: 0, column: 0 };
+ let mut end = InfoPosition { line: 0, column: 0 };
let mut filename = String::new();
let mut message = String::new();
let mut severity = Severity::Warning;
@@ -74,8 +81,18 @@ impl Info {
if let Some(source) = diagnostic.source_code() {
if let Some(label) = labels.next() {
if let Ok(span_content) = source.read_span(label.inner(), 0, 0) {
- line = span_content.line() + 1;
- column = span_content.column() + 1;
+ start.line = span_content.line() + 1;
+ start.column = span_content.column() + 1;
+
+ let end_offset = label.inner().offset() + label.inner().len();
+
+ if let Ok(span_content) =
+ source.read_span(&SourceSpan::from((end_offset, 0)), 0, 0)
+ {
+ end.line = span_content.line() + 1;
+ end.column = span_content.column() + 1;
+ }
+
if let Some(name) = span_content.name() {
filename = name.to_string();
};
@@ -92,6 +109,7 @@ impl Info {
}
}
}
- Self { line, column, filename, message, severity, rule_id }
+
+ Self { start, end, filename, message, severity, rule_id }
}
}