feat(when): single when clause now emits warning
This commit is contained in:
parent
ad7a62d2bf
commit
7206360baa
|
@ -1,7 +1,7 @@
|
||||||
use super::Type;
|
use super::Type;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Annotation, BinOp, CallArg, Span, TodoKind, UntypedPattern},
|
ast::{Annotation, BinOp, CallArg, Span, TodoKind, UntypedPattern},
|
||||||
expr,
|
expr::{self, UntypedExpr},
|
||||||
format::Formatter,
|
format::Formatter,
|
||||||
levenshtein,
|
levenshtein,
|
||||||
pretty::Documentable,
|
pretty::Documentable,
|
||||||
|
@ -172,20 +172,7 @@ You can use '{discard}' and numbers to distinguish between similar names.
|
||||||
If you really meant to return that last expression, try to replace it with the following:
|
If you really meant to return that last expression, try to replace it with the following:
|
||||||
|
|
||||||
{sample}"#
|
{sample}"#
|
||||||
, sample = Formatter::new()
|
, sample = format_suggestion(expr)
|
||||||
.expr(expr)
|
|
||||||
.to_pretty_string(70)
|
|
||||||
.lines()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(ix, line)| {
|
|
||||||
if ix == 0 {
|
|
||||||
format!("╰─▶ {}", line.yellow())
|
|
||||||
} else {
|
|
||||||
format!(" {line}").yellow().to_string()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("")
|
|
||||||
))]
|
))]
|
||||||
LastExpressionIsAssignment {
|
LastExpressionIsAssignment {
|
||||||
#[label("let-binding as last expression")]
|
#[label("let-binding as last expression")]
|
||||||
|
@ -1034,14 +1021,12 @@ 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 todo left in the code.\n")]
|
#[error("I found a record update using all fields; thus redundant.\n")]
|
||||||
#[diagnostic(help("You probably want to replace that one with real code... eventually."))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
|
||||||
#[diagnostic(code("todo"))]
|
#[diagnostic(code("record_update::all_fields"))]
|
||||||
Todo {
|
AllFieldsRecordUpdate {
|
||||||
kind: TodoKind,
|
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
tipo: Arc<Type>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error(
|
#[error(
|
||||||
|
@ -1056,9 +1041,10 @@ pub enum Warning {
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I found a literal that is unused.\n")]
|
#[error("I found a record update with no fields; effectively updating nothing.\n")]
|
||||||
#[diagnostic(code("unused::literal"))]
|
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
|
||||||
UnusedLiteral {
|
#[diagnostic(code("record_update::no_fields"))]
|
||||||
|
NoFieldsRecordUpdate {
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
@ -1070,29 +1056,25 @@ pub enum Warning {
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I found a record update with no fields; effectively updating nothing.\n")]
|
#[error("I found a when expression with a single clause.")]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
|
#[diagnostic(
|
||||||
#[diagnostic(code("record_update::no_fields"))]
|
code("single_when_clause"),
|
||||||
NoFieldsRecordUpdate {
|
help("Prefer using a {} binding like so...\n\n{}", "let".purple(), format_suggestion(sample))
|
||||||
|
)]
|
||||||
|
SingleWhenClause {
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
|
sample: UntypedExpr,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I found a record update using all fields; thus redundant.\n")]
|
#[error("I found a todo left in the code.\n")]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#record-updates"))]
|
#[diagnostic(help("You probably want to replace that one with real code... eventually."))]
|
||||||
#[diagnostic(code("record_update::all_fields"))]
|
#[diagnostic(code("todo"))]
|
||||||
AllFieldsRecordUpdate {
|
Todo {
|
||||||
|
kind: TodoKind,
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
tipo: Arc<Type>,
|
||||||
|
|
||||||
#[error("I discovered an unused type: '{}'.\n", name.purple())]
|
|
||||||
#[diagnostic(code("unused::type"))]
|
|
||||||
UnusedType {
|
|
||||||
#[label]
|
|
||||||
location: Span,
|
|
||||||
imported: bool,
|
|
||||||
name: String,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I discovered an unused constructor: '{}'.\n", name.purple())]
|
#[error("I discovered an unused constructor: '{}'.\n", name.purple())]
|
||||||
|
@ -1107,6 +1089,17 @@ pub enum Warning {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("I discovered an unused imported module: '{}'.\n", name.purple())]
|
||||||
|
#[diagnostic(help(
|
||||||
|
"No big deal, but you might want to remove it to get rid of that warning."
|
||||||
|
))]
|
||||||
|
#[diagnostic(code("unused::import::module"))]
|
||||||
|
UnusedImportedModule {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("I discovered an unused imported value: '{}'.\n", name.purple())]
|
#[error("I discovered an unused imported value: '{}'.\n", name.purple())]
|
||||||
#[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."
|
||||||
|
@ -1118,12 +1111,21 @@ pub enum Warning {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I discovered an unused imported module: '{}'.\n", name.purple())]
|
#[error("I found a literal that is unused.\n")]
|
||||||
|
#[diagnostic(code("unused::literal"))]
|
||||||
|
UnusedLiteral {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("I found an unused private function: '{}'.\n", name.purple())]
|
||||||
#[diagnostic(help(
|
#[diagnostic(help(
|
||||||
"No big deal, but you might want to remove it to get rid of that warning."
|
"Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\
|
||||||
|
Otherwise, you might want to get rid of it altogether."
|
||||||
|
, keyword_pub = "pub".bright_blue()
|
||||||
))]
|
))]
|
||||||
#[diagnostic(code("unused::import::module"))]
|
#[diagnostic(code("unused::function"))]
|
||||||
UnusedImportedModule {
|
UnusedPrivateFunction {
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -1142,16 +1144,12 @@ pub enum Warning {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("I found an unused private function: '{}'.\n", name.purple())]
|
#[error("I discovered an unused type: '{}'.\n", name.purple())]
|
||||||
#[diagnostic(help(
|
#[diagnostic(code("unused::type"))]
|
||||||
"Perhaps your forgot to make it public using the '{keyword_pub}' keyword?\n\
|
UnusedType {
|
||||||
Otherwise, you might want to get rid of it altogether."
|
|
||||||
, keyword_pub = "pub".bright_blue()
|
|
||||||
))]
|
|
||||||
#[diagnostic(code("unused::function"))]
|
|
||||||
UnusedPrivateFunction {
|
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
|
imported: bool,
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1187,3 +1185,20 @@ pub enum UnknownRecordFieldSituation {
|
||||||
/// This unknown record field is being called as a function. i.e. `record.field()`
|
/// This unknown record field is being called as a function. i.e. `record.field()`
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_suggestion<'a>(sample: &'a UntypedExpr) -> String {
|
||||||
|
Formatter::new()
|
||||||
|
.expr(sample)
|
||||||
|
.to_pretty_string(70)
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(ix, line)| {
|
||||||
|
if ix == 0 {
|
||||||
|
format!("╰─▶ {}", line.yellow())
|
||||||
|
} else {
|
||||||
|
format!(" {line}").yellow().to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("")
|
||||||
|
}
|
||||||
|
|
|
@ -1930,6 +1930,21 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
clauses: Vec<UntypedClause>,
|
clauses: Vec<UntypedClause>,
|
||||||
location: Span,
|
location: Span,
|
||||||
) -> Result<TypedExpr, Error> {
|
) -> Result<TypedExpr, Error> {
|
||||||
|
// if there is only one clause we want to present a warning
|
||||||
|
// that suggests that a `let` binding should be used instead.
|
||||||
|
if clauses.len() == 1 {
|
||||||
|
self.environment.warnings.push(Warning::SingleWhenClause {
|
||||||
|
location: clauses[0].location,
|
||||||
|
sample: UntypedExpr::Assignment {
|
||||||
|
location: Span::empty(),
|
||||||
|
value: Box::new(subjects[0].clone()),
|
||||||
|
pattern: clauses[0].pattern[0].clone(),
|
||||||
|
kind: AssignmentKind::Let,
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let subjects_count = subjects.len();
|
let subjects_count = subjects.len();
|
||||||
|
|
||||||
let mut typed_subjects = Vec::with_capacity(subjects_count);
|
let mut typed_subjects = Vec::with_capacity(subjects_count);
|
||||||
|
|
Loading…
Reference in New Issue