Enumerate severity on problem variants

This commit is contained in:
Ayaz Hafiz 2022-12-02 11:14:17 -06:00
parent 0a4ec1958b
commit e438fbf37c
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
9 changed files with 92 additions and 74 deletions

View file

@ -46,7 +46,7 @@ use roc_parse::ident::UppercaseIdent;
use roc_parse::module::module_defs;
use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError};
use roc_region::all::{LineInfo, Loc, Region};
use roc_reporting::report::{Annotation, Palette, RenderTarget};
use roc_reporting::report::{Annotation, Palette, RenderTarget, Severity};
use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule};
use roc_solve_problem::TypeError;
use roc_target::TargetInfo;

View file

@ -7,6 +7,8 @@ use roc_parse::pattern::PatternType;
use roc_region::all::{Loc, Region};
use roc_types::types::AliasKind;
use crate::Severity;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CycleEntry {
pub symbol: Symbol,
@ -205,6 +207,70 @@ pub enum Problem {
}
impl Problem {
pub fn severity(&self) -> Severity {
use Severity::{RuntimeError, Warning};
match self {
Problem::UnusedDef(_, _) => Warning,
Problem::UnusedImport(_, _) => Warning,
Problem::UnusedModuleImport(_, _) => Warning,
Problem::ExposedButNotDefined(_) => Warning,
Problem::UnknownGeneratesWith(_) => RuntimeError,
Problem::UnusedArgument(_, _, _, _) => Warning,
Problem::UnusedBranchDef(_, _) => Warning,
Problem::PrecedenceProblem(_) => RuntimeError,
Problem::UnsupportedPattern(_, _) => RuntimeError,
Problem::Shadowing { .. } => RuntimeError,
Problem::CyclicAlias(..) => RuntimeError,
Problem::BadRecursion(_) => RuntimeError,
Problem::PhantomTypeArgument { .. } => Warning,
Problem::UnboundTypeVariable { .. } => RuntimeError,
Problem::DuplicateRecordFieldValue { .. } => Warning,
Problem::DuplicateRecordFieldType { .. } => RuntimeError,
Problem::InvalidOptionalValue { .. } => RuntimeError,
Problem::DuplicateTag { .. } => RuntimeError,
Problem::RuntimeError(_) => RuntimeError,
Problem::SignatureDefMismatch { .. } => RuntimeError,
Problem::InvalidAliasRigid { .. } => RuntimeError,
Problem::InvalidInterpolation(_) => RuntimeError,
Problem::InvalidHexadecimal(_) => RuntimeError,
Problem::InvalidUnicodeCodePt(_) => RuntimeError,
Problem::NestedDatatype { .. } => RuntimeError,
Problem::InvalidExtensionType { .. } => RuntimeError,
Problem::AbilityHasTypeVariables { .. } => RuntimeError,
Problem::HasClauseIsNotAbility { .. } => RuntimeError,
Problem::IllegalHasClause { .. } => RuntimeError,
Problem::DuplicateHasAbility { .. } => Warning,
Problem::AbilityMemberMissingHasClause { .. } => RuntimeError,
Problem::AbilityMemberMultipleBoundVars { .. } => RuntimeError,
Problem::AbilityNotOnToplevel { .. } => RuntimeError, // Ideally, could be compiled
Problem::AbilityUsedAsType(_, _, _) => RuntimeError,
Problem::NestedSpecialization(_, _) => RuntimeError, // Ideally, could be compiled
Problem::IllegalDerivedAbility(_) => RuntimeError,
Problem::ImplementationNotFound { .. } => RuntimeError,
Problem::NotAnAbilityMember { .. } => RuntimeError,
Problem::OptionalAbilityImpl { .. } => RuntimeError,
Problem::QualifiedAbilityImpl { .. } => RuntimeError,
Problem::AbilityImplNotIdent { .. } => RuntimeError,
Problem::DuplicateImpl { .. } => Warning, // First impl is used at runtime
Problem::NotAnAbility(_) => Warning,
Problem::ImplementsNonRequired { .. } => Warning,
Problem::DoesNotImplementAbility { .. } => RuntimeError,
Problem::NotBoundInAllPatterns { .. } => RuntimeError,
Problem::NoIdentifiersIntroduced(_) => Warning,
Problem::OverloadedSpecialization { .. } => Warning, // Ideally, will compile
Problem::UnnecessaryOutputWildcard { .. } => Warning,
// TODO: sometimes this can just be a warning, e.g. if you have [1, .., .., 2] but we
// don't catch that yet.
Problem::MultipleListRestPattern { .. } => RuntimeError,
Problem::BadTypeArguments { .. } => RuntimeError,
// TODO: this can be a warning instead if we recover the program by
// injecting a crash message
Problem::UnappliedCrash { .. } => RuntimeError,
Problem::OverAppliedCrash { .. } => RuntimeError,
}
}
/// Returns a Region value from the Problem, if possible.
/// Some problems have more than one region; in those cases,
/// this tries to pick the one that's closest to the original

View file

@ -3,3 +3,15 @@
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)]
pub mod can;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Severity {
/// This will cause a runtime error if some code get srun
/// (e.g. type mismatch, naming error)
RuntimeError,
/// This will never cause the code to misbehave,
/// but should be cleaned up
/// (e.g. unused def, unused import)
Warning,
}

View file

@ -29,9 +29,8 @@ pub fn report_problems(
can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
type_problems: &mut MutMap<ModuleId, Vec<TypeError>>,
) -> Problems {
use crate::report::{
can_problem, type_problem, Report, RocDocAllocator, Severity::*, DEFAULT_PALETTE,
};
use crate::report::{can_problem, type_problem, Report, RocDocAllocator, DEFAULT_PALETTE};
use roc_problem::Severity::*;
let palette = DEFAULT_PALETTE;
// This will often over-allocate total memory, but it means we definitely

View file

@ -6,12 +6,13 @@ use roc_problem::can::{
BadPattern, CycleEntry, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError,
ShadowKind,
};
use roc_problem::Severity;
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
use roc_types::types::AliasKind;
use std::path::PathBuf;
use crate::error::r#type::suggest;
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder};
use ven_pretty::DocAllocator;
const SYNTAX_PROBLEM: &str = "SYNTAX PROBLEM";
@ -67,7 +68,7 @@ pub fn can_problem<'b>(
) -> Report<'b> {
let doc;
let title;
let severity;
let severity = problem.severity();
match problem {
Problem::UnusedDef(symbol, region) => {
@ -86,7 +87,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_DEF.to_string();
severity = Severity::Warning;
}
Problem::UnusedImport(symbol, region) => {
doc = alloc.stack([
@ -103,7 +103,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_IMPORT.to_string();
severity = Severity::Warning;
}
Problem::UnusedModuleImport(module_id, region) => {
doc = alloc.stack([
@ -121,7 +120,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_IMPORT.to_string();
severity = Severity::Warning;
}
Problem::DefsOnlyUsedInRecursion(1, region) => {
doc = alloc.stack([
@ -165,7 +163,6 @@ pub fn can_problem<'b>(
]);
title = MISSING_DEFINITION.to_string();
severity = Severity::RuntimeError;
}
Problem::UnknownGeneratesWith(loc_ident) => {
doc = alloc.stack([
@ -180,7 +177,6 @@ pub fn can_problem<'b>(
]);
title = UNKNOWN_GENERATES_WITH.to_string();
severity = Severity::RuntimeError;
}
Problem::UnusedArgument(closure_symbol, is_anonymous, argument_symbol, region) => {
let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used.";
@ -215,7 +211,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_ARG.to_string();
severity = Severity::Warning;
}
Problem::UnusedBranchDef(symbol, region) => {
doc = alloc.stack([
@ -236,7 +231,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_DEF.to_string();
severity = Severity::Warning;
}
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => {
doc = alloc.stack([
@ -265,7 +259,6 @@ pub fn can_problem<'b>(
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::UnsupportedPattern(BadPattern::Unsupported(pattern_type), region) => {
use roc_parse::pattern::PatternType::*;
@ -296,7 +289,6 @@ pub fn can_problem<'b>(
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::Shadowing {
original_region,
@ -308,7 +300,6 @@ pub fn can_problem<'b>(
doc = res_doc;
title = res_title.to_string();
severity = Severity::RuntimeError;
}
Problem::CyclicAlias(symbol, region, others, alias_kind) => {
let answer = crate::error::r#type::cyclic_alias(
@ -317,7 +308,6 @@ pub fn can_problem<'b>(
doc = answer.0;
title = answer.1;
severity = Severity::RuntimeError;
}
Problem::PhantomTypeArgument {
typ: alias,
@ -345,7 +335,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_ALIAS_PARAM.to_string();
severity = Severity::RuntimeError;
}
Problem::UnboundTypeVariable {
typ: alias,
@ -382,12 +371,10 @@ pub fn can_problem<'b>(
doc = alloc.stack(stack);
title = UNBOUND_TYPE_VARIABLE.to_string();
severity = Severity::RuntimeError;
}
Problem::BadRecursion(entries) => {
doc = to_circular_def_doc(alloc, lines, &entries);
title = CIRCULAR_DEF.to_string();
severity = Severity::RuntimeError;
}
Problem::DuplicateRecordFieldValue {
field_name,
@ -422,7 +409,6 @@ pub fn can_problem<'b>(
]);
title = DUPLICATE_FIELD_NAME.to_string();
severity = Severity::Warning;
}
Problem::InvalidOptionalValue {
field_name,
@ -471,7 +457,6 @@ pub fn can_problem<'b>(
]);
title = DUPLICATE_FIELD_NAME.to_string();
severity = Severity::Warning;
}
Problem::DuplicateTag {
tag_name,
@ -506,7 +491,6 @@ pub fn can_problem<'b>(
]);
title = DUPLICATE_TAG_NAME.to_string();
severity = Severity::Warning;
}
Problem::SignatureDefMismatch {
ref annotation_pattern,
@ -523,7 +507,6 @@ pub fn can_problem<'b>(
]);
title = NAMING_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidAliasRigid {
alias_name: type_name,
@ -546,7 +529,6 @@ pub fn can_problem<'b>(
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidHexadecimal(region) => {
doc = alloc.stack([
@ -563,7 +545,6 @@ pub fn can_problem<'b>(
]);
title = INVALID_UNICODE.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidUnicodeCodePt(region) => {
doc = alloc.stack([
@ -573,7 +554,6 @@ pub fn can_problem<'b>(
]);
title = INVALID_UNICODE.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidInterpolation(region) => {
doc = alloc.stack([
@ -590,14 +570,12 @@ pub fn can_problem<'b>(
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::RuntimeError(runtime_error) => {
let answer = pretty_runtime_error(alloc, lines, runtime_error);
doc = answer.0;
title = answer.1.to_string();
severity = Severity::RuntimeError;
}
Problem::NestedDatatype {
alias,
@ -625,7 +603,6 @@ pub fn can_problem<'b>(
]);
title = NESTED_DATATYPE.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidExtensionType { region, kind } => {
@ -653,7 +630,6 @@ pub fn can_problem<'b>(
]);
title = INVALID_EXTENSION_TYPE.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityHasTypeVariables {
@ -672,7 +648,6 @@ pub fn can_problem<'b>(
),
]);
title = ABILITY_HAS_TYPE_VARIABLES.to_string();
severity = Severity::RuntimeError;
}
Problem::HasClauseIsNotAbility {
@ -683,7 +658,6 @@ pub fn can_problem<'b>(
alloc.region(lines.convert_region(clause_region)),
]);
title = HAS_CLAUSE_IS_NOT_AN_ABILITY.to_string();
severity = Severity::RuntimeError;
}
Problem::IllegalHasClause { region } => {
@ -702,7 +676,6 @@ pub fn can_problem<'b>(
]),
]);
title = ILLEGAL_HAS_CLAUSE.to_string();
severity = Severity::RuntimeError;
}
Problem::DuplicateHasAbility { ability, region } => {
@ -720,7 +693,6 @@ pub fn can_problem<'b>(
]),
]);
title = "DUPLICATE BOUND ABILITY".to_string();
severity = Severity::Warning;
}
Problem::AbilityMemberMissingHasClause {
@ -755,7 +727,6 @@ pub fn can_problem<'b>(
.reflow("Otherwise, the function does not need to be part of the ability!")]),
]);
title = ABILITY_MEMBER_MISSING_HAS_CLAUSE.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityMemberMultipleBoundVars {
@ -783,7 +754,6 @@ pub fn can_problem<'b>(
])
]);
title = ABILITY_MEMBER_BINDS_MULTIPLE_VARIABLES.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityNotOnToplevel { region } => {
@ -795,7 +765,6 @@ pub fn can_problem<'b>(
alloc.reflow("Abilities can only be defined on the top-level of a Roc module."),
]);
title = ABILITY_NOT_ON_TOPLEVEL.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityUsedAsType(suggested_var_name, ability, region) => {
@ -823,7 +792,6 @@ pub fn can_problem<'b>(
])),
]);
title = ABILITY_USED_AS_TYPE.to_string();
severity = Severity::RuntimeError;
}
Problem::NestedSpecialization(member, region) => {
doc = alloc.stack([
@ -836,7 +804,6 @@ pub fn can_problem<'b>(
alloc.reflow("Specializations can only be defined on the top-level of a module."),
]);
title = SPECIALIZATION_NOT_ON_TOPLEVEL.to_string();
severity = Severity::Warning;
}
Problem::IllegalDerivedAbility(region) => {
doc = alloc.stack([
@ -848,7 +815,6 @@ pub fn can_problem<'b>(
.append(list_builtin_abilities(alloc)),
]);
title = ILLEGAL_DERIVE.to_string();
severity = Severity::Warning;
}
Problem::NotAnAbility(region) => {
doc = alloc.stack([
@ -857,7 +823,6 @@ pub fn can_problem<'b>(
alloc.reflow("Only abilities can be implemented."),
]);
title = NOT_AN_ABILITY.to_string();
severity = Severity::Warning;
}
Problem::NotAnAbilityMember {
ability,
@ -872,7 +837,6 @@ pub fn can_problem<'b>(
alloc.reflow("Only implementations for members an ability has can be specified in this location.")
]);
title = NOT_AN_ABILITY_MEMBER.to_string();
severity = Severity::RuntimeError;
}
Problem::ImplementationNotFound { member, region } => {
let member_str = member.as_str(alloc.interns);
@ -884,7 +848,6 @@ pub fn can_problem<'b>(
alloc.tip().append(alloc.concat([alloc.reflow("consider adding a value of name "), alloc.symbol_unqualified(member), alloc.reflow(" in this scope, or using another variable that implements this ability member, like "), alloc.type_str(&format!("{{ {}: my{} }}", member_str, member_str))]))
]);
title = IMPLEMENTATION_NOT_FOUND.to_string();
severity = Severity::RuntimeError;
}
Problem::OptionalAbilityImpl { ability, region } => {
let hint = if ability.is_builtin() {
@ -903,7 +866,6 @@ pub fn can_problem<'b>(
hint,
]);
title = OPTIONAL_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
}
Problem::QualifiedAbilityImpl { region } => {
doc = alloc.stack([
@ -914,7 +876,6 @@ pub fn can_problem<'b>(
),
]);
title = QUALIFIED_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityImplNotIdent { region } => {
doc = alloc.stack([
@ -926,7 +887,6 @@ pub fn can_problem<'b>(
alloc.tip().append(alloc.reflow("consider defining this expression as a variable."))
]);
title = ABILITY_IMPLEMENTATION_NOT_IDENTIFIER.to_string();
severity = Severity::RuntimeError;
}
Problem::DuplicateImpl {
original,
@ -941,7 +901,6 @@ pub fn can_problem<'b>(
.reflow("Only one custom implementation can be defined for an ability member."),
]);
title = DUPLICATE_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
}
Problem::ImplementsNonRequired {
region,
@ -966,7 +925,6 @@ pub fn can_problem<'b>(
),
]);
title = UNNECESSARY_IMPLEMENTATIONS.to_string();
severity = Severity::Warning;
}
Problem::DoesNotImplementAbility {
region,
@ -991,7 +949,6 @@ pub fn can_problem<'b>(
),
]);
title = INCOMPLETE_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
}
Problem::NotBoundInAllPatterns {
unbound_symbol,
@ -1012,7 +969,6 @@ pub fn can_problem<'b>(
]),
]);
title = "NAME NOT BOUND IN ALL PATTERNS".to_string();
severity = Severity::RuntimeError;
}
Problem::NoIdentifiersIntroduced(region) => {
doc = alloc.stack([
@ -1021,7 +977,6 @@ pub fn can_problem<'b>(
alloc.reflow("If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior!"),
]);
title = "UNNECESSARY DEFINITION".to_string();
severity = Severity::Warning;
}
Problem::OverloadedSpecialization {
ability_member,
@ -1041,7 +996,6 @@ pub fn can_problem<'b>(
alloc.reflow("Ability specializations can only provide implementations for one opaque type, since all opaque types are different!"),
]);
title = "OVERLOADED SPECIALIZATION".to_string();
severity = Severity::Warning;
}
Problem::UnnecessaryOutputWildcard { region } => {
doc = alloc.stack([
@ -1061,7 +1015,6 @@ pub fn can_problem<'b>(
alloc.reflow("You can safely remove this to make the code more concise without changing what it means."),
]);
title = "UNNECESSARY WILDCARD".to_string();
severity = Severity::Warning;
}
Problem::MultipleListRestPattern { region } => {
doc = alloc.stack([
@ -1074,7 +1027,6 @@ pub fn can_problem<'b>(
]),
]);
title = "MULTIPLE LIST REST PATTERNS".to_string();
severity = Severity::RuntimeError;
}
Problem::BadTypeArguments {
symbol,
@ -1114,7 +1066,6 @@ pub fn can_problem<'b>(
} else {
"TOO FEW TYPE ARGUMENTS".to_string()
};
severity = Severity::RuntimeError;
}
Problem::UnappliedCrash { region } => {
doc = alloc.stack([
@ -1128,7 +1079,6 @@ pub fn can_problem<'b>(
])
]);
title = "UNAPPLIED CRASH".to_string();
severity = Severity::RuntimeError;
}
Problem::OverAppliedCrash { region } => {
doc = alloc.stack([
@ -1144,7 +1094,6 @@ pub fn can_problem<'b>(
]),
]);
title = "OVERAPPLIED CRASH".to_string();
severity = Severity::RuntimeError;
}
};

View file

@ -3,6 +3,7 @@ use std::path::PathBuf;
use bumpalo::Bump;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_parse::ast::Expr;
use roc_problem::Severity;
use roc_region::all::{LineColumnRegion, LineInfo, Region};
use roc_types::{
subs::{Subs, Variable},
@ -154,7 +155,7 @@ impl<'a> Renderer<'a> {
title: "EXPECT FAILED".into(),
doc,
filename: self.filename.clone(),
severity: crate::report::Severity::RuntimeError,
severity: Severity::RuntimeError,
};
let mut buf = String::new();
@ -225,7 +226,7 @@ impl<'a> Renderer<'a> {
title: "EXPECT PANICKED".into(),
doc,
filename: self.filename.clone(),
severity: crate::report::Severity::RuntimeError,
severity: Severity::RuntimeError,
};
let mut buf = String::new();

View file

@ -1,8 +1,9 @@
use roc_parse::parser::{ENumber, FileError, PList, SyntaxError};
use roc_problem::Severity;
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Position, Region};
use std::path::PathBuf;
use crate::report::{Report, RocDocAllocator, RocDocBuilder, Severity};
use crate::report::{Report, RocDocAllocator, RocDocBuilder};
use ven_pretty::DocAllocator;
pub fn parse_problem<'a>(

View file

@ -1,5 +1,5 @@
use crate::error::canonicalize::{to_circular_def_doc, CIRCULAR_DEF};
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder};
use roc_can::expected::{Expected, PExpected};
use roc_collections::all::{HumanIndex, MutSet, SendMap};
use roc_collections::VecMap;
@ -8,6 +8,7 @@ use roc_exhaustive::{CtorName, ListArity};
use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::{IdentStr, Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_problem::Severity;
use roc_region::all::{LineInfo, Region};
use roc_solve_problem::{
NotDerivableContext, NotDerivableDecode, NotDerivableEq, TypeError, UnderivableReason,

View file

@ -1,6 +1,7 @@
use roc_module::ident::Ident;
use roc_module::ident::{Lowercase, ModuleName, TagName, Uppercase};
use roc_module::symbol::{Interns, ModuleId, PQModuleName, PackageQualified, Symbol};
use roc_problem::Severity;
use roc_region::all::LineColumnRegion;
use std::fmt;
use std::path::{Path, PathBuf};
@ -99,18 +100,6 @@ pub fn pretty_header_with_path(title: &str, path: &Path) -> String {
header
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Severity {
/// This will cause a runtime error if some code get srun
/// (e.g. type mismatch, naming error)
RuntimeError,
/// This will never cause the code to misbehave,
/// but should be cleaned up
/// (e.g. unused def, unused import)
Warning,
}
#[derive(Clone, Copy, Debug)]
pub enum RenderTarget {
ColorTerminal,