Suggest possible candidate on unknown imports.

## Before

  ```
  × Type-checking
  ╰─▶ Unknown module field 'ValidityRaneg' in module 'aiken/transaction'
  ```

  ## After

  ```
    × Type-checking
    ╰─▶ Unknown import 'ValidityRaneg' from module 'aiken/transaction'

     ╭─[../stdlib/validators/tmp.ak:2:1]
   2 │ use aiken/interval.{Interval, IntervalBound, IntervalBoundType}
   3 │ use aiken/transaction.{ScriptContext, ValidityRaneg}
     ·                                       ─────────────
   4 │
     ╰────
    help: Did you mean to import 'ValidityRange'?
  ```
This commit is contained in:
KtorZ 2022-12-22 23:46:13 +01:00
parent 70b1ec4324
commit aa2a235790
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
2 changed files with 47 additions and 19 deletions

View File

@ -783,21 +783,13 @@ impl<'a> Environment<'a> {
}; };
} else if !value_imported { } else if !value_imported {
// Error if no type or value was found with that name // Error if no type or value was found with that name
return Err(Error::UnknownModuleField { return Err(Error::unknown_module_field(
location: *location, *location,
name: name.clone(), name.clone(),
module_name: module.join("/"), module.join("/"),
value_constructors: module_info module_info.values.keys().map(|t| t.to_string()).collect(),
.values module_info.types.keys().map(|t| t.to_string()).collect(),
.keys() ));
.map(|t| t.to_string())
.collect(),
type_constructors: module_info
.types
.keys()
.map(|t| t.to_string())
.collect(),
});
} }
} }

View File

@ -4,7 +4,10 @@ use ordinal::Ordinal;
use miette::Diagnostic; use miette::Diagnostic;
use crate::ast::{BinOp, Span, TodoKind}; use crate::{
ast::{BinOp, Span, TodoKind},
levenshtein,
};
use super::Type; use super::Type;
@ -175,13 +178,15 @@ pub enum Error {
imported_modules: Vec<String>, imported_modules: Vec<String>,
}, },
#[error("Unknown module field\n\n{name}\n\nin module\n\n{module_name}\n")] #[error("Unknown import '{name}' from module '{module_name}'\n")]
#[diagnostic()]
UnknownModuleField { UnknownModuleField {
#[label]
location: Span, location: Span,
name: String, name: String,
module_name: String, module_name: String,
value_constructors: Vec<String>, #[help]
type_constructors: Vec<String>, hint: Option<String>,
}, },
#[error("Unknown module value\n\n{name}\n")] #[error("Unknown module value\n\n{name}\n")]
@ -419,6 +424,37 @@ impl Error {
other => other, other => other,
} }
} }
pub fn unknown_module_field(
location: Span,
name: String,
module_name: String,
value_constructors: Vec<String>,
type_constructors: Vec<String>,
) -> Self {
let mut candidates = vec![];
candidates.extend(value_constructors);
candidates.extend(type_constructors);
let hint = candidates
.iter()
.map(|s| (s, levenshtein::distance(&name, s)))
.min_by(|(_, a), (_, b)| a.cmp(b))
.and_then(|(suggestion, distance)| {
if distance <= 5 {
Some(format!("Did you mean to import '{suggestion}'?"))
} else {
None
}
});
Self::UnknownModuleField {
location,
name,
module_name,
hint,
}
}
} }
#[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)] #[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)]