summaryrefslogtreecommitdiff
path: root/cli/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/diagnostics.rs')
-rw-r--r--cli/diagnostics.rs187
1 files changed, 118 insertions, 69 deletions
diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs
index b0b284360..aca86913e 100644
--- a/cli/diagnostics.rs
+++ b/cli/diagnostics.rs
@@ -6,9 +6,7 @@
// serde_json.
use crate::colors;
-use crate::fmt_errors::format_maybe_source_line;
-use crate::fmt_errors::format_maybe_source_name;
-use crate::fmt_errors::DisplayFormatter;
+use crate::fmt_errors::format_stack;
use serde_json::value::Value;
use std::error::Error;
use std::fmt;
@@ -56,14 +54,14 @@ impl fmt::Display for Diagnostic {
let mut i = 0;
for item in &self.items {
if i > 0 {
- writeln!(f)?;
+ write!(f, "\n\n")?;
}
write!(f, "{}", item.to_string())?;
i += 1;
}
if i > 1 {
- write!(f, "\n\nFound {} errors.\n", i)?;
+ write!(f, "\n\nFound {} errors.", i)?;
}
Ok(())
@@ -181,91 +179,126 @@ impl DiagnosticItem {
}
}
-impl DisplayFormatter for DiagnosticItem {
- fn format_category_and_code(&self) -> String {
- let category = match self.category {
- DiagnosticCategory::Error => {
- format!("{}", colors::red_bold("error".to_string()))
- }
- DiagnosticCategory::Warning => "warn".to_string(),
- DiagnosticCategory::Debug => "debug".to_string(),
- DiagnosticCategory::Info => "info".to_string(),
- _ => "".to_string(),
- };
+fn format_category_and_code(
+ category: &DiagnosticCategory,
+ code: i64,
+) -> String {
+ let category = match category {
+ DiagnosticCategory::Error => {
+ format!("{}", colors::red_bold("error".to_string()))
+ }
+ DiagnosticCategory::Warning => "warn".to_string(),
+ DiagnosticCategory::Debug => "debug".to_string(),
+ DiagnosticCategory::Info => "info".to_string(),
+ _ => "".to_string(),
+ };
- let code =
- colors::bold(format!(" TS{}", self.code.to_string())).to_string();
+ let code = colors::bold(format!("TS{}", code.to_string())).to_string();
- format!("{}{}: ", category, code)
+ format!("{} {}", category, code)
+}
+
+fn format_message(
+ message_chain: &Option<DiagnosticMessageChain>,
+ message: &str,
+ level: usize,
+) -> String {
+ debug!("format_message");
+ if message_chain.is_none() {
+ return format!("{:indent$}{}", "", message, indent = level);
}
- fn format_message(&self, level: usize) -> String {
- debug!("format_message");
- if self.message_chain.is_none() {
- return format!("{:indent$}{}", "", self.message, indent = level);
- }
+ let mut s = message_chain.clone().unwrap().format_message(level);
+ s.pop();
- let mut s = self.message_chain.clone().unwrap().format_message(level);
- s.pop();
+ s
+}
- s
+/// Formats optional source, line and column numbers into a single string.
+fn format_maybe_frame(
+ file_name: Option<String>,
+ line_number: Option<i64>,
+ column_number: Option<i64>,
+) -> String {
+ if file_name.is_none() {
+ return "".to_string();
}
- fn format_related_info(&self) -> String {
- if self.related_information.is_none() {
- return "".to_string();
- }
+ assert!(line_number.is_some());
+ assert!(column_number.is_some());
- let mut s = String::new();
- let related_information = self.related_information.clone().unwrap();
- for related_diagnostic in related_information {
- let rd = &related_diagnostic;
- s.push_str(&format!(
- "\n{}\n\n ► {}{}\n",
- rd.format_message(2),
- rd.format_source_name(),
- rd.format_source_line(4),
- ));
- }
+ let line_number = line_number.unwrap();
+ let column_number = column_number.unwrap();
+ let file_name_c = colors::cyan(file_name.unwrap());
+ let line_c = colors::yellow(line_number.to_string());
+ let column_c = colors::yellow(column_number.to_string());
+ format!("{}:{}:{}", file_name_c, line_c, column_c)
+}
- s
+fn format_maybe_related_information(
+ related_information: &Option<Vec<DiagnosticItem>>,
+) -> String {
+ if related_information.is_none() {
+ return "".to_string();
}
- fn format_source_line(&self, level: usize) -> String {
- // Formatter expects 1-based line numbers, but ours are 0-based.
- format_maybe_source_line(
- self.source_line.clone(),
- self.line_number.map(|n| n + 1),
- self.start_column,
- self.end_column,
- match self.category {
+ let mut s = String::new();
+ let related_information = related_information.clone().unwrap();
+ for rd in related_information {
+ s.push_str("\n\n");
+ s.push_str(&format_stack(
+ match rd.category {
DiagnosticCategory::Error => true,
_ => false,
},
- level,
- )
+ format_message(&rd.message_chain, &rd.message, 0),
+ rd.source_line.clone(),
+ rd.start_column,
+ rd.end_column,
+ // Formatter expects 1-based line and column numbers, but ours are 0-based.
+ &[format_maybe_frame(
+ rd.script_resource_name.clone(),
+ rd.line_number.map(|n| n + 1),
+ rd.start_column.map(|n| n + 1),
+ )],
+ 4,
+ ));
}
- fn format_source_name(&self) -> String {
- // Formatter expects 1-based line and column numbers, but ours are 0-based.
- format_maybe_source_name(
- self.script_resource_name.clone(),
- self.line_number.map(|n| n + 1),
- self.start_column.map(|n| n + 1),
- )
- }
+ s
}
impl fmt::Display for DiagnosticItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
- "{}{}\n\n► {}{}{}",
- self.format_category_and_code(),
- self.format_message(0),
- self.format_source_name(),
- self.format_source_line(0),
- self.format_related_info(),
+ "{}",
+ format_stack(
+ match self.category {
+ DiagnosticCategory::Error => true,
+ _ => false,
+ },
+ format!(
+ "{}: {}",
+ format_category_and_code(&self.category, self.code),
+ format_message(&self.message_chain, &self.message, 0)
+ ),
+ self.source_line.clone(),
+ self.start_column,
+ self.end_column,
+ // Formatter expects 1-based line and column numbers, but ours are 0-based.
+ &[format_maybe_frame(
+ self.script_resource_name.clone(),
+ self.line_number.map(|n| n + 1),
+ self.start_column.map(|n| n + 1)
+ )],
+ 0
+ )
+ )?;
+ write!(
+ f,
+ "{}",
+ format_maybe_related_information(&self.related_information),
)
}
}
@@ -575,14 +608,30 @@ mod tests {
#[test]
fn diagnostic_to_string1() {
let d = diagnostic1();
- let expected = "error TS2322: Type \'(o: T) => { v: any; f: (x: B) => string; }[]\' is not assignable to type \'(r: B) => Value<B>[]\'.\n Types of parameters \'o\' and \'r\' are incompatible.\n Type \'B\' is not assignable to type \'T\'.\n\n► deno/tests/complex_diagnostics.ts:19:3\n\n19 values: o => [\n ~~~~~~\n\n The expected type comes from property \'values\' which is declared here on type \'SettingsInterface<B>\'\n\n ► deno/tests/complex_diagnostics.ts:7:3\n\n 7 values?: (r: T) => Array<Value<T>>;\n ~~~~~~\n\n";
+ let expected = "error TS2322: Type \'(o: T) => { v: any; f: (x: B) => string; }[]\' is not assignable to type \'(r: B) => Value<B>[]\'.\n Types of parameters \'o\' and \'r\' are incompatible.\n Type \'B\' is not assignable to type \'T\'.\n values: o => [\n ~~~~~~\n at deno/tests/complex_diagnostics.ts:19:3\n\n The expected type comes from property \'values\' which is declared here on type \'SettingsInterface<B>\'\n values?: (r: T) => Array<Value<T>>;\n ~~~~~~\n at deno/tests/complex_diagnostics.ts:7:3";
assert_eq!(expected, strip_ansi_codes(&d.to_string()));
}
#[test]
fn diagnostic_to_string2() {
let d = diagnostic2();
- let expected = "error TS2322: Example 1\n\n► deno/tests/complex_diagnostics.ts:19:3\n\n19 values: o => [\n ~~~~~~\n\nerror TS2000: Example 2\n\n► /foo/bar.ts:129:3\n\n129 values: undefined,\n ~~~~~~\n\n\nFound 2 errors.\n";
+ let expected = "error TS2322: Example 1\n values: o => [\n ~~~~~~\n at deno/tests/complex_diagnostics.ts:19:3\n\nerror TS2000: Example 2\n values: undefined,\n ~~~~~~\n at /foo/bar.ts:129:3\n\nFound 2 errors.";
assert_eq!(expected, strip_ansi_codes(&d.to_string()));
}
+
+ #[test]
+ fn test_format_none_frame() {
+ let actual = format_maybe_frame(None, None, None);
+ assert_eq!(actual, "");
+ }
+
+ #[test]
+ fn test_format_some_frame() {
+ let actual = format_maybe_frame(
+ Some("file://foo/bar.ts".to_string()),
+ Some(1),
+ Some(2),
+ );
+ assert_eq!(strip_ansi_codes(&actual), "file://foo/bar.ts:1:2");
+ }
}