Preserve warning display rework, but without breaking the LSP quickfixes.

This commit is contained in:
KtorZ 2024-05-30 19:20:11 +02:00
parent 649e5163fc
commit e9e26b969a
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
2 changed files with 140 additions and 81 deletions

View File

@ -1475,31 +1475,29 @@ fn suggest_import_constructor() -> String {
#[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)] #[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)]
pub enum Warning { pub enum Warning {
#[error("I found a record update using all fields; thus redundant.\n")] #[error("I found a record update using all fields; thus redundant.")]
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
#[diagnostic(code("record_update::all_fields"))] #[diagnostic(code("record_update::all_fields"))]
AllFieldsRecordUpdate { AllFieldsRecordUpdate {
#[label] #[label("redundant record update")]
location: Span, location: Span,
}, },
#[error( #[error("I realized the following expression returned a result that is implicitly discarded.")]
"I realized the following expression returned a result that is implicitly discarded.\n"
)]
#[diagnostic(help( #[diagnostic(help(
"You can use the '_' symbol should you want to explicitly discard a result." "You can use the '_' symbol should you want to explicitly discard a result."
))] ))]
#[diagnostic(code("implicit_discard"))] #[diagnostic(code("implicit_discard"))]
ImplicitlyDiscardedResult { ImplicitlyDiscardedResult {
#[label] #[label("implicitly discarded result")]
location: Span, location: Span,
}, },
#[error("I found a record update with no fields; effectively updating nothing.\n")] #[error("I found a record update with no fields; effectively updating nothing.")]
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))] #[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
#[diagnostic(code("record_update::no_fields"))] #[diagnostic(code("record_update::no_fields"))]
NoFieldsRecordUpdate { NoFieldsRecordUpdate {
#[label] #[label("useless record update")]
location: Span, location: Span,
}, },
@ -1519,17 +1517,20 @@ pub enum Warning {
}, },
#[error( #[error(
"I found an {} trying to match a type with one constructor", "I found an {} {}",
"expect".if_supports_color(Stderr, |s| s.purple()) "expect".if_supports_color(Stderr, |s| s.purple()),
"trying to match a type with one constructor".if_supports_color(Stderr, |s| s.yellow())
)] )]
#[diagnostic( #[diagnostic(
code("single_constructor_expect"), code("single_constructor_expect"),
help( help(
"If your type has one constructor, unless you are casting {} {}, you can\nprefer using a {} binding like so...\n\n{}", "If your type has one constructor, unless you are casting {} {}, you can\nprefer using a {} binding like so...\n\n{}",
"from".if_supports_color(Stderr, |s| s.bold()), "from".if_supports_color(Stderr, |s| s.bold()),
"Data".if_supports_color(Stderr, |s| s.bright_blue()), "Data"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.bright_blue()),
"let".if_supports_color(Stderr, |s| s.purple()), "let".if_supports_color(Stderr, |s| s.purple()),
format_suggestion(sample) format_suggestion(sample),
) )
)] )]
SingleConstructorExpect { SingleConstructorExpect {
@ -1542,7 +1543,7 @@ pub enum Warning {
sample: UntypedExpr, sample: UntypedExpr,
}, },
#[error("I found a todo left in the code.\n")] #[error("I found a todo left in the code.")]
#[diagnostic(help("You probably want to replace that with actual code... eventually."))] #[diagnostic(help("You probably want to replace that with actual code... eventually."))]
#[diagnostic(code("todo"))] #[diagnostic(code("todo"))]
Todo { Todo {
@ -1551,7 +1552,7 @@ pub enum Warning {
tipo: Rc<Type>, tipo: Rc<Type>,
}, },
#[error("I found a type hole in an annotation.\n")] #[error("I found a type hole in an annotation.")]
#[diagnostic(code("unexpected::type_hole"))] #[diagnostic(code("unexpected::type_hole"))]
UnexpectedTypeHole { UnexpectedTypeHole {
#[label("{}", tipo.to_pretty(0))] #[label("{}", tipo.to_pretty(0))]
@ -1560,93 +1561,95 @@ pub enum Warning {
}, },
#[error( #[error(
"I discovered an unused constructor: '{}'.\n", "I discovered an unused constructor: {}",
name.if_supports_color(Stderr, |s| s.purple()) name.if_supports_color(Stderr, |s| s.default_color())
)] )]
#[diagnostic(help( #[diagnostic(help(
"No big deal, but you might want to remove it to get rid of that warning." "No big deal, but you might want to remove it to get rid of that warning."
))] ))]
#[diagnostic(code("unused::constructor"))] #[diagnostic(code("unused::constructor"))]
UnusedConstructor { UnusedConstructor {
#[label] #[label("unused constructor")]
location: Span, location: Span,
name: String, name: String,
}, },
#[error( #[error(
"I discovered an unused imported module: '{}'.\n", "I discovered an unused imported module: {}",
name.if_supports_color(Stderr, |s| s.purple()) name.if_supports_color(Stderr, |s| s.default_color()),
)] )]
#[diagnostic(help( #[diagnostic(help(
"No big deal, but you might want to remove it to get rid of that warning." "No big deal, but you might want to remove it to get rid of that warning."
))] ))]
#[diagnostic(code("unused::import::module"))] #[diagnostic(code("unused::import::module"))]
UnusedImportedModule { UnusedImportedModule {
#[label] #[label("unused module")]
location: Span, location: Span,
name: String, name: String,
}, },
#[error( #[error(
"I discovered an unused imported value: '{}'.\n", "I discovered an unused imported value: {}",
name.if_supports_color(Stderr, |s| s.purple()), name.if_supports_color(Stderr, |s| s.default_color()),
)] )]
#[diagnostic(help( #[diagnostic(help(
"No big deal, but you might want to remove it to get rid of that warning." "No big deal, but you might want to remove it to get rid of that warning."
))] ))]
#[diagnostic(code("unused:import::value"))] #[diagnostic(code("unused:import::value"))]
UnusedImportedValueOrType { UnusedImportedValueOrType {
#[label] #[label("unused import")]
location: Span, location: Span,
name: String, name: String,
}, },
#[error( #[error(
"I found an unused private function: '{}'.\n", "I found an unused private function: {}",
name.if_supports_color(Stderr, |s| s.purple()), name.if_supports_color(Stderr, |s| s.default_color()),
)] )]
#[diagnostic(help( #[diagnostic(help(
"Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\ "Perhaps your forgot to make it public using the {keyword_pub} keyword?\n\
Otherwise, you might want to get rid of it altogether.", Otherwise, you might want to get rid of it altogether.",
keyword_pub = "pub".if_supports_color(Stderr, |s| s.bright_blue()) keyword_pub = "pub".if_supports_color(Stderr, |s| s.bright_blue())
))] ))]
#[diagnostic(code("unused::function"))] #[diagnostic(code("unused::function"))]
UnusedPrivateFunction { UnusedPrivateFunction {
#[label] #[label("unused (private) function")]
location: Span, location: Span,
name: String, name: String,
}, },
#[error( #[error(
"I found an unused (private) module constant: '{}'.\n", "I found an unused (private) module constant: {}",
name.if_supports_color(Stderr, |s| s.purple()) name.if_supports_color(Stderr, |s| s.default_color()),
)] )]
#[diagnostic(help( #[diagnostic(help(
"Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\ "Perhaps your forgot to make it public using the {keyword_pub} keyword?\n\
Otherwise, you might want to get rid of it altogether.", Otherwise, you might want to get rid of it altogether.",
keyword_pub = "pub".if_supports_color(Stderr, |s| s.bright_blue()) keyword_pub = "pub".if_supports_color(Stderr, |s| s.bright_blue())
))] ))]
#[diagnostic(code("unused::constant"))] #[diagnostic(code("unused::constant"))]
UnusedPrivateModuleConstant { UnusedPrivateModuleConstant {
#[label] #[label("unused (private) constant")]
location: Span, location: Span,
name: String, name: String,
}, },
#[error( #[error(
"I discovered an unused type: '{}'.\n", "I discovered an unused type: {}",
name.if_supports_color(Stderr, |s| s.purple()) name
.if_supports_color(Stderr, |s| s.bright_blue())
.if_supports_color(Stderr, |s| s.bold())
)] )]
#[diagnostic(code("unused::type"))] #[diagnostic(code("unused::type"))]
UnusedType { UnusedType {
#[label] #[label("unused (private) type")]
location: Span, location: Span,
name: String, name: String,
}, },
#[error( #[error(
"I came across an unused variable: {}.\n", "I came across an unused variable: {}",
name.if_supports_color(Stderr, |s| s.purple()) name.if_supports_color(Stderr, |s| s.default_color()),
)] )]
#[diagnostic(help("{}", formatdoc! { #[diagnostic(help("{}", formatdoc! {
r#"No big deal, but you might want to remove it or use a discard {name} to get rid of that warning. r#"No big deal, but you might want to remove it or use a discard {name} to get rid of that warning.
@ -1663,14 +1666,14 @@ pub enum Warning {
}))] }))]
#[diagnostic(code("unused::variable"))] #[diagnostic(code("unused::variable"))]
UnusedVariable { UnusedVariable {
#[label("unused")] #[label("unused identifier")]
location: Span, location: Span,
name: String, name: String,
}, },
#[error( #[error(
"I came across a discarded variable in a let assignment: {}.\n", "I came across a discarded variable in a let assignment: {}",
name.if_supports_color(Stderr, |s| s.purple()) name.if_supports_color(Stderr, |s| s.default_color())
)] )]
#[diagnostic(help("{}", formatdoc! { #[diagnostic(help("{}", formatdoc! {
r#"If you do want to enforce some side-effects, use {keyword_expect} with {name} instead of {keyword_let}. r#"If you do want to enforce some side-effects, use {keyword_expect} with {name} instead of {keyword_let}.
@ -1685,14 +1688,15 @@ pub enum Warning {
}))] }))]
#[diagnostic(code("unused::discarded_let_assignment"))] #[diagnostic(code("unused::discarded_let_assignment"))]
DiscardedLetAssignment { DiscardedLetAssignment {
#[label("discarded")] #[label("discarded result")]
location: Span, location: Span,
name: String, name: String,
}, },
#[error( #[error(
"I came across a validator in a {} module which means\nI'm going to ignore it.\n", "I came across a validator in a {} {}",
"lib/".if_supports_color(Stderr, |s| s.purple()) "lib/".if_supports_color(Stderr, |s| s.purple()),
"module which means\nI'm going to ignore it.\n".if_supports_color(Stderr, |s| s.yellow()),
)] )]
#[diagnostic(help( #[diagnostic(help(
"No big deal, but you might want to move it to a {} module\nor remove it to get rid of that warning.", "No big deal, but you might want to move it to a {} module\nor remove it to get rid of that warning.",
@ -1700,15 +1704,16 @@ pub enum Warning {
))] ))]
#[diagnostic(code("unused::validator"))] #[diagnostic(code("unused::validator"))]
ValidatorInLibraryModule { ValidatorInLibraryModule {
#[label("unused")] #[label("unused validator")]
location: Span, location: Span,
}, },
#[error( #[error(
"I noticed a suspicious {type_ByteArray} UTF-8 literal which resembles a hash digest.", "I noticed a suspicious {type_ByteArray} {tail}",
type_ByteArray = "ByteArray" type_ByteArray = "ByteArray"
.if_supports_color(Stderr, |s| s.bright_blue()) .if_supports_color(Stderr, |s| s.bright_blue())
.if_supports_color(Stderr, |s| s.bold()) .if_supports_color(Stderr, |s| s.bold()),
tail = "UTF-8 literal which resembles a hash digest.".if_supports_color(Stderr, |s| s.yellow()),
)] )]
#[diagnostic(help("{}", formatdoc! { #[diagnostic(help("{}", formatdoc! {
r#"When you specify a {type_ByteArray} literal using plain double-quotes, it's interpreted as an array of UTF-8 bytes. For example, the literal {literal_foo} is interpreted as the byte sequence {foo_bytes}. r#"When you specify a {type_ByteArray} literal using plain double-quotes, it's interpreted as an array of UTF-8 bytes. For example, the literal {literal_foo} is interpreted as the byte sequence {foo_bytes}.
@ -1793,11 +1798,9 @@ fn format_suggestion(sample: &UntypedExpr) -> String {
.enumerate() .enumerate()
.map(|(ix, line)| { .map(|(ix, line)| {
if ix == 0 { if ix == 0 {
format!("╰─▶ {}", line.if_supports_color(Stdout, |s| s.yellow())) format!("╰─▶ {line}")
} else { } else {
format!(" {line}") format!(" {line}")
.if_supports_color(Stdout, |s| s.yellow())
.to_string()
} }
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()

View File

@ -1,4 +1,4 @@
use crate::{blueprint::error as blueprint, deps::manifest::Package, package_name::PackageName}; use crate::{blueprint, deps::manifest::Package, package_name::PackageName};
use aiken_lang::{ use aiken_lang::{
ast::{self, Span}, ast::{self, Span},
error::ExtraData, error::ExtraData,
@ -6,7 +6,8 @@ use aiken_lang::{
tipo, tipo,
}; };
use miette::{ use miette::{
Diagnostic, EyreContext, LabeledSpan, MietteHandlerOpts, NamedSource, RgbColors, SourceCode, Diagnostic, EyreContext, LabeledSpan, MietteHandler, MietteHandlerOpts, NamedSource, RgbColors,
SourceCode,
}; };
use owo_colors::{ use owo_colors::{
OwoColorize, OwoColorize,
@ -157,20 +158,11 @@ impl Error {
impl Debug for Error { impl Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let miette_handler = MietteHandlerOpts::new() default_miette_handler(2)
// For better support of terminal themes use the ANSI coloring .debug(self, f)
.rgb_colors(RgbColors::Never)
// If ansi support is disabled in the config disable the eye-candy
.color(true)
.unicode(true)
.terminal_links(true)
.build();
// Ignore error to prevent format! panics. This can happen if span points at some // Ignore error to prevent format! panics. This can happen if span points at some
// inaccessible location, for example by calling `report_error()` with wrong working set. // inaccessible location, for example by calling `report_error()` with wrong working set.
let _ = miette_handler.debug(self, f); .or(Ok(()))
Ok(())
} }
} }
@ -504,9 +496,9 @@ impl Diagnostic for Error {
#[derive(thiserror::Error)] #[derive(thiserror::Error)]
pub enum Warning { pub enum Warning {
#[error("{}", "You do not have any validators to build!".if_supports_color(Stderr, |s| s.yellow()))] #[error("You do not have any validators to build!")]
NoValidators, NoValidators,
#[error("{}", "While trying to make sense of your code...".if_supports_color(Stderr, |s| s.yellow()))] #[error("{}", warning)]
Type { Type {
path: PathBuf, path: PathBuf,
src: String, src: String,
@ -514,9 +506,9 @@ pub enum Warning {
#[source] #[source]
warning: tipo::error::Warning, warning: tipo::error::Warning,
}, },
#[error("{}", format!("{name} is already a dependency.").if_supports_color(Stderr, |s| s.yellow()))] #[error("{name} is already a dependency.")]
DependencyAlreadyExists { name: PackageName }, DependencyAlreadyExists { name: PackageName },
#[error("{}", format!("Ignoring file with invalid module name at: {path:?}").if_supports_color(Stderr, |s| s.yellow()))] #[error("Ignoring file with invalid module name at: {path:?}")]
InvalidModuleName { path: PathBuf }, InvalidModuleName { path: PathBuf },
} }
@ -573,7 +565,17 @@ impl Diagnostic for Warning {
} }
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
None match self {
Warning::Type { warning, .. } => Some(Box::new(format!(
"aiken::check{}",
warning.code().map(|s| format!("::{s}")).unwrap_or_default()
))),
Warning::NoValidators => Some(Box::new("aiken::check")),
Warning::InvalidModuleName { .. } => Some(Box::new("aiken::project::module_name")),
Warning::DependencyAlreadyExists { .. } => {
Some(Box::new("aiken::packages::already_exists"))
}
}
} }
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
@ -607,20 +609,63 @@ impl Warning {
impl Debug for Warning { impl Debug for Warning {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let miette_handler = MietteHandlerOpts::new() default_miette_handler(0)
// For better support of terminal themes use the ANSI coloring .debug(
.rgb_colors(RgbColors::Never) &DisplayWarning {
// If ansi support is disabled in the config disable the eye-candy title: &self.to_string(),
.color(true) source_code: self.source_code(),
.unicode(true) labels: self.labels().map(|ls| ls.collect()),
.terminal_links(true) help: self.help().map(|s| s.to_string()),
.build(); },
f,
)
// Ignore error to prevent format! panics. This can happen if span points at some // Ignore error to prevent format! panics. This can happen if span points at some
// inaccessible location, for example by calling `report_error()` with wrong working set. // inaccessible location, for example by calling `report_error()` with wrong working set.
let _ = miette_handler.debug(self, f); .or(Ok(()))
}
}
Ok(()) #[derive(thiserror::Error)]
#[error("{}", title.if_supports_color(Stderr, |s| s.yellow()))]
struct DisplayWarning<'a> {
title: &'a str,
source_code: Option<&'a dyn miette::SourceCode>,
labels: Option<Vec<LabeledSpan>>,
help: Option<String>,
}
impl<'a> Diagnostic for DisplayWarning<'a> {
fn severity(&self) -> Option<miette::Severity> {
Some(miette::Severity::Warning)
}
fn source_code(&self) -> Option<&dyn SourceCode> {
self.source_code
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
self.labels
.as_ref()
.map(|ls| ls.iter().cloned())
.map(Box::new)
.map(|b| b as Box<dyn Iterator<Item = LabeledSpan>>)
}
fn code<'b>(&'b self) -> Option<Box<dyn Display + 'b>> {
None
}
fn help<'b>(&'b self) -> Option<Box<dyn Display + 'b>> {
self.help
.as_ref()
.map(Box::new)
.map(|b| b as Box<dyn Display + 'b>)
}
}
impl<'a> Debug for DisplayWarning<'a> {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unreachable!("Display warning are never shown directly.");
} }
} }
@ -631,3 +676,14 @@ pub struct Unformatted {
pub input: String, pub input: String,
pub output: String, pub output: String,
} }
fn default_miette_handler(context_lines: usize) -> MietteHandler {
MietteHandlerOpts::new()
// For better support of terminal themes use the ANSI coloring
.rgb_colors(RgbColors::Never)
// If ansi support is disabled in the config disable the eye-candy
.unicode(true)
.terminal_links(true)
.context_lines(context_lines)
.build()
}