diff --git a/compiler/reporting/src/report.rs b/compiler/reporting/src/report.rs index 47743afae5..e264ed9aff 100644 --- a/compiler/reporting/src/report.rs +++ b/compiler/reporting/src/report.rs @@ -1,6 +1,6 @@ +use crate::report::ReportText::{Batch, Region, Value}; use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_problem::can::Problem; -use roc_region::all::Region; use roc_types::pretty_print::content_to_string; use roc_types::subs::{Content, Subs}; use std::path::PathBuf; @@ -11,19 +11,32 @@ pub struct Report { pub text: ReportText, } -impl Report { - pub fn can_problem(_filename: PathBuf, _problem: Problem) -> Self { - // let text = match problem { - // Problem::UnusedDef(symbol, region) => { - // panic!("TODO implelment me!"); - // } - // _ => { - // panic!("TODO implement others"); - // } - // }; +pub fn can_problem(filename: PathBuf, problem: Problem) -> Report { + let mut texts = Vec::new(); - // Report { filename, text } - panic!("TODO implement me!"); + match problem { + Problem::UnusedDef(symbol, region) => { + texts.push(Value(symbol)); + texts.push(plain_text(" is not used anywhere in your code.")); + texts.push(newline()); + texts.push(newline()); + texts.push(Region(region)); + texts.push(newline()); + texts.push(newline()); + texts.push(plain_text("If you didn't intend on using ")); + texts.push(Value(symbol)); + texts.push(plain_text( + " then remove it so future readers of your code don't wonder why it is there.", + )); + } + _ => { + panic!("TODO implement others"); + } + }; + + Report { + filename, + text: Batch(texts), } } @@ -43,13 +56,25 @@ pub enum ReportText { EmText(Box), /// A region in the original source - Region(Region), + Region(roc_region::all::Region), /// A URL, which should be rendered as a hyperlink. Url(Box), /// The documentation for this symbol. Docs(Symbol), + + Batch(Vec), +} + +pub fn plain_text(str: &str) -> ReportText { + use ReportText::*; + + Plain(Box::from(str)) +} + +fn newline() -> ReportText { + plain_text("\n") } impl ReportText { @@ -89,7 +114,7 @@ impl ReportText { } Type(content) => buf.push_str(content_to_string(content, subs, home, interns).as_str()), Region(region) => { - for i in region.start_line..region.end_line { + for i in region.start_line..=region.end_line { buf.push_str(i.to_string().as_str()); buf.push_str(" |"); @@ -100,12 +125,19 @@ impl ReportText { buf.push_str(src_lines[i as usize]); } - buf.push('\n'); + if i != region.end_line { + buf.push('\n'); + } } } Docs(_) => { panic!("TODO implment docs"); } + Batch(report_texts) => { + for report_text in report_texts { + report_text.render_ci(buf, subs, home, src_lines, interns); + } + } } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 6d0ef66cf8..8b9708ddc9 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -11,24 +11,30 @@ mod helpers; mod test_report { use crate::helpers::test_home; use roc_module::symbol::{Interns, ModuleId}; - use roc_reporting::report::{Report, ReportText}; + use roc_reporting::report::{can_problem, plain_text, Report, ReportText}; use roc_types::pretty_print::name_all_type_vars; use roc_types::subs::Subs; use roc_types::types; use std::path::PathBuf; // use roc_region::all; use crate::helpers::{assert_correct_variable_usage, can_expr, infer_expr, CanExprOut}; - use roc_reporting::report::ReportText::{EmText, Plain, Region, Type, Url, Value}; + use roc_problem::can::Problem; + use roc_reporting::report::ReportText::{Batch, EmText, Plain, Region, Type, Url, Value}; use roc_types::subs::Content::{FlexVar, RigidVar, Structure}; use roc_types::subs::FlatType::EmptyRecord; + fn filename_from_string(str: &str) -> PathBuf { + let mut filename = PathBuf::new(); + filename.push(str); + + return filename; + } + // use roc_problem::can; fn to_simple_report(text: ReportText) -> Report { - let mut filename = PathBuf::new(); - filename.push(r"\code\proj\Main.roc"); Report { text: text, - filename: filename, + filename: filename_from_string(r"\code\proj\Main.roc"), } } @@ -69,7 +75,7 @@ mod test_report { fn report_renders_as_from_src(src: &str, report: Report, expected_rendering: &str) { let (_type_problems, _can_problems, mut subs, home, interns) = infer_expr_help(src); - let mut buf = String::new(); + let mut buf: String = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); report @@ -96,7 +102,7 @@ mod test_report { #[test] fn report_plain() { - report_renders_as(to_simple_report(Plain(Box::from("y"))), "y"); + report_renders_as(to_simple_report(plain_text("y")), "y"); } #[test] @@ -152,6 +158,62 @@ mod test_report { report_renders_as(to_simple_report(Type(Structure(EmptyRecord))), "{}"); } + #[test] + fn report_batch_of_plain_text() { + let mut report_texts = Vec::new(); + + report_texts.push(plain_text("Wait a second. ")); + report_texts.push(plain_text("There is a problem here. -> ")); + report_texts.push(EmText(Box::from("y"))); + + report_renders_as( + to_simple_report(Batch(report_texts)), + "Wait a second. There is a problem here. -> *y*", + ); + } + + #[test] + fn report_unused_def() { + let src: &str = indoc!( + r#" + x = 1 + y = 2 + + x + "# + ); + + let (_type_problems, can_problems, mut subs, home, interns) = infer_expr_help(src); + + let mut buf: String = String::new(); + let src_lines: Vec<&str> = src.split('\n').collect(); + + match can_problems.first() { + None => {} + Some(problem) => { + let report = can_problem( + filename_from_string(r"\code\proj\Main.roc"), + problem.clone(), + ); + report + .text + .render_ci(&mut buf, &mut subs, home, &src_lines, &interns) + } + } + + assert_eq!( + buf, + indoc!( + r#" + y is not used anywhere in your code. + + 1 | y = 2 + + If you didn't intend on using y then remove it so future readers of your code don't wonder why it is there."# + ) + ); + } + #[test] fn report_region() { report_renders_as_from_src( @@ -166,7 +228,7 @@ mod test_report { ), to_simple_report(Region(roc_region::all::Region { start_line: 1, - end_line: 4, + end_line: 3, start_col: 0, end_col: 0, })),