mirror of
https://github.com/oxalica/nil.git
synced 2024-11-27 00:06:26 +03:00
Report name resolution failures
This commit is contained in:
parent
c91cf603e6
commit
b2128038c2
@ -21,6 +21,7 @@ Super fast incremental analysis! Scans `all-packages.nix` in less than 0.1s and
|
||||
- Syntax errors.
|
||||
- Incomplete syntax errors are currently suppressed to avoid noisy outputs during typing.
|
||||
- [x] Hard semantic errors reported as parse errors by Nix, like duplicated keys in attrsets.
|
||||
- [x] Undefiend names.
|
||||
- [x] Warnings of legacy syntax.
|
||||
- [x] Warnings of unnecessary syntax.
|
||||
- [x] Warnings of unused bindings, `with` and `rec`.
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{BindingKey, BindingValue, Bindings, DefDatabase, Expr, ExprId, Module, NameDefId};
|
||||
use crate::{builtin, FileId};
|
||||
use crate::{builtin, Diagnostic, DiagnosticKind, FileId};
|
||||
use la_arena::{Arena, ArenaMap, Idx};
|
||||
use smol_str::SmolStr;
|
||||
use std::collections::HashMap;
|
||||
@ -210,7 +210,8 @@ impl ScopeData {
|
||||
/// Name resolution of all references.
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NameResolution {
|
||||
resolve_map: HashMap<ExprId, ResolveResult>,
|
||||
// `None` value for unresolved names.
|
||||
resolve_map: HashMap<ExprId, Option<ResolveResult>>,
|
||||
}
|
||||
|
||||
impl NameResolution {
|
||||
@ -222,7 +223,7 @@ impl NameResolution {
|
||||
.filter_map(|(e, kind)| {
|
||||
match kind {
|
||||
// Inherited attrs are also translated into Expr::References.
|
||||
Expr::Reference(name) => Some((e, scopes.resolve_name(e, name)?)),
|
||||
Expr::Reference(name) => Some((e, scopes.resolve_name(e, name))),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
@ -231,11 +232,29 @@ impl NameResolution {
|
||||
}
|
||||
|
||||
pub fn get(&self, expr: ExprId) -> Option<&ResolveResult> {
|
||||
self.resolve_map.get(&expr)
|
||||
self.resolve_map.get(&expr)?.as_ref()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (ExprId, &'_ ResolveResult)> + '_ {
|
||||
self.resolve_map.iter().map(|(e, res)| (*e, res))
|
||||
self.resolve_map
|
||||
.iter()
|
||||
.filter_map(|(e, res)| Some((*e, res.as_ref()?)))
|
||||
}
|
||||
|
||||
pub fn to_diagnostics(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
file_id: FileId,
|
||||
) -> impl Iterator<Item = Diagnostic> + '_ {
|
||||
let source_map = db.source_map(file_id);
|
||||
self.resolve_map
|
||||
.iter()
|
||||
.filter(|(_, res)| res.is_none())
|
||||
.filter_map(move |(&e, _)| {
|
||||
let ptr = source_map.expr_node(e)?;
|
||||
let range = ptr.text_range();
|
||||
Some(Diagnostic::new(range, DiagnosticKind::UndefinedName))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,9 @@ pub enum DiagnosticKind {
|
||||
MergePlainRecAttrset,
|
||||
MergeRecAttrset,
|
||||
|
||||
// Name resolution.
|
||||
UndefinedName,
|
||||
|
||||
// Liveness.
|
||||
UnusedBinding,
|
||||
UnusedWith,
|
||||
@ -52,7 +55,9 @@ impl Diagnostic {
|
||||
|
||||
pub fn severity(&self) -> Severity {
|
||||
match self.kind {
|
||||
DiagnosticKind::InvalidDynamic | DiagnosticKind::DuplicatedKey => Severity::Error,
|
||||
DiagnosticKind::InvalidDynamic
|
||||
| DiagnosticKind::DuplicatedKey
|
||||
| DiagnosticKind::UndefinedName => Severity::Error,
|
||||
DiagnosticKind::EmptyInherit
|
||||
| DiagnosticKind::EmptyLetIn
|
||||
| DiagnosticKind::LetAttrset
|
||||
@ -97,6 +102,8 @@ impl Diagnostic {
|
||||
"Merging rec-attrset with other attrsets or attrpath. Merged values can unexpectedly reference each other remotely as in a single `rec { ... }`"
|
||||
}
|
||||
|
||||
DiagnosticKind::UndefinedName => "Undefined name",
|
||||
|
||||
DiagnosticKind::UnusedBinding => "Unused binding",
|
||||
DiagnosticKind::UnusedWith => "Unused `with`",
|
||||
DiagnosticKind::UnusedRec => "Unused `rec`",
|
||||
|
@ -11,6 +11,9 @@ pub(crate) fn diagnostics(db: &dyn DefDatabase, file: FileId) -> Vec<Diagnostic>
|
||||
let module = db.module(file);
|
||||
diags.extend(module.diagnostics().iter().cloned());
|
||||
|
||||
// Name resolution.
|
||||
diags.extend(db.name_resolution(file).to_diagnostics(db, file));
|
||||
|
||||
// Liveness check.
|
||||
let liveness = db.liveness_check(file);
|
||||
diags.extend(liveness.to_diagnostics(db, file));
|
||||
@ -56,6 +59,16 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn name_resolution() {
|
||||
check(
|
||||
"a",
|
||||
expect![[r#"
|
||||
0..1: Undefined name
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn liveness() {
|
||||
check(
|
||||
|
Loading…
Reference in New Issue
Block a user