mirror of
https://github.com/oxalica/nil.git
synced 2024-11-26 02:48:06 +03:00
Impl scope calculation for bindings
This commit is contained in:
parent
73d71845f6
commit
63e01c3411
125
src/def/scope.rs
125
src/def/scope.rs
@ -1,4 +1,4 @@
|
||||
use super::{DefDatabase, Expr, ExprId, Module, NameDefId};
|
||||
use super::{BindingKey, BindingValue, Bindings, DefDatabase, Expr, ExprId, Module, NameDefId};
|
||||
use crate::base::FileId;
|
||||
use la_arena::{Arena, ArenaMap, Idx};
|
||||
use smol_str::SmolStr;
|
||||
@ -105,9 +105,59 @@ impl ModuleScopes {
|
||||
});
|
||||
self.traverse_expr(module, *body, scope);
|
||||
}
|
||||
Expr::Attrset(bindings) | Expr::LetAttrset(bindings) => {
|
||||
self.traverse_bindings(module, bindings, scope);
|
||||
}
|
||||
Expr::LetIn(bindings, body) => {
|
||||
let scope = self.traverse_bindings(module, bindings, scope);
|
||||
self.traverse_expr(module, *body, scope);
|
||||
}
|
||||
e => e.walk_child_exprs(|e| self.traverse_expr(module, e, scope)),
|
||||
}
|
||||
}
|
||||
|
||||
fn traverse_bindings(
|
||||
&mut self,
|
||||
module: &Module,
|
||||
bindings: &Bindings,
|
||||
scope: ScopeId,
|
||||
) -> ScopeId {
|
||||
let mut defs = HashMap::default();
|
||||
|
||||
for (k, v) in bindings.entries.iter() {
|
||||
if let &BindingKey::NameDef(def) = k {
|
||||
defs.insert(module[def].name.clone(), def);
|
||||
}
|
||||
|
||||
// Inherited attrs are resolved in the outer scope.
|
||||
if let &BindingValue::Inherit(expr) = v {
|
||||
assert!(matches!(&module[expr], Expr::Reference(_)));
|
||||
self.traverse_expr(module, expr, scope);
|
||||
}
|
||||
}
|
||||
|
||||
let scope = if defs.is_empty() {
|
||||
scope
|
||||
} else {
|
||||
self.scopes.alloc(ScopeData {
|
||||
parent: Some(scope),
|
||||
kind: ScopeKind::NameDefs(defs),
|
||||
})
|
||||
};
|
||||
|
||||
for (k, v) in bindings.entries.iter() {
|
||||
if let &BindingKey::Dynamic(expr) = k {
|
||||
self.traverse_expr(module, expr, scope);
|
||||
}
|
||||
if let &BindingValue::Expr(expr) = v {
|
||||
self.traverse_expr(module, expr, scope);
|
||||
}
|
||||
}
|
||||
for &e in bindings.inherit_froms.iter() {
|
||||
self.traverse_expr(module, e, scope);
|
||||
}
|
||||
scope
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -150,7 +200,7 @@ mod tests {
|
||||
};
|
||||
use expect_test::{expect, Expect};
|
||||
use rowan::ast::AstNode;
|
||||
use syntax::ast;
|
||||
use syntax::{ast, match_ast};
|
||||
|
||||
#[track_caller]
|
||||
fn check_scopes(src: &str, expect: Expect) {
|
||||
@ -191,7 +241,20 @@ mod tests {
|
||||
#[track_caller]
|
||||
fn check_resolve(src: &str, expect: Option<u32>) {
|
||||
let (db, file_id, pos) = TestDB::from_file_with_pos(src);
|
||||
let ptr = AstPtr::new(db.node_at::<ast::Expr>(file_id, pos).syntax());
|
||||
|
||||
// Inherit(Attr(Name)) or Expr(Ref(Name))
|
||||
let ptr = db
|
||||
.find_node(file_id, pos, |n| {
|
||||
match_ast! {
|
||||
match n {
|
||||
ast::Expr(e) => Some(AstPtr::new(e.syntax())),
|
||||
ast::Attr(e) => Some(AstPtr::new(e.syntax())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
})
|
||||
.expect("No Attr or Expr found");
|
||||
|
||||
let parse = db.parse(file_id).value;
|
||||
let source_map = db.source_map(file_id);
|
||||
let expr_id = source_map.expr_map[&ptr];
|
||||
@ -239,4 +302,60 @@ mod tests {
|
||||
check_resolve(r"x: with a; with b; $0x", Some(0));
|
||||
check_resolve(r"x: with a; with b; $0y", Some(16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attrset_non_rec() {
|
||||
check_scopes(
|
||||
"a: { inherit a; b = c: $0a; e = 1; inherit (a) f; }",
|
||||
expect!["c@20 | a@0"],
|
||||
);
|
||||
check_scopes(
|
||||
"a: { inherit a; b = c: a; e = 1; inherit ($0a) f; }",
|
||||
expect!["a@0"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attrset_rec() {
|
||||
check_scopes(
|
||||
"a: rec { inherit a; b = c: $0a; e = 1; inherit (a) f; }",
|
||||
expect!["c@24 | a@17 b@20 e@30 f@49 | a@0"],
|
||||
);
|
||||
check_scopes(
|
||||
"a: rec { inherit a; b = c: a; e = 1; inherit ($0a) f; }",
|
||||
expect!["a@17 b@20 e@30 f@49 | a@0"],
|
||||
);
|
||||
check_scopes(
|
||||
"a: rec { inherit $0a; b = c: a; e = 1; inherit (a) f; }",
|
||||
expect!["a@0"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_in() {
|
||||
check_scopes(r#"let a.b = 1; "c+d" = a; in $0a"#, expect!["a@4 c+d@13"]);
|
||||
check_scopes(r#"let a.b = 1; "b+c" = $0a; in a"#, expect!["a@4 b+c@13"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadowing() {
|
||||
check_scopes(
|
||||
"let a = 1; b = 2; in let a = 2; inherit b; in $0a",
|
||||
expect!["a@25 b@40 | a@4 b@11"],
|
||||
);
|
||||
|
||||
check_resolve(
|
||||
"let a = 1; b = 2; in let a = 2; inherit b; in $0a",
|
||||
Some(25),
|
||||
);
|
||||
check_resolve(
|
||||
"let a = 1; b = 2; in let a = 2; inherit b; in $0b",
|
||||
Some(40),
|
||||
);
|
||||
check_resolve(
|
||||
"let a = 1; b = 2; in let a = 2; inherit $0b; in b",
|
||||
Some(11),
|
||||
);
|
||||
check_resolve("let a = 1; in let a = $0a; in a", Some(18));
|
||||
}
|
||||
}
|
||||
|
15
src/tests.rs
15
src/tests.rs
@ -2,7 +2,7 @@ use crate::base::{SourceDatabase, SourceDatabaseStorage};
|
||||
use crate::def::DefDatabaseStorage;
|
||||
use crate::{Change, FileId};
|
||||
use rowan::{ast::AstNode, TextSize};
|
||||
use syntax::NixLanguage;
|
||||
use syntax::{NixLanguage, SyntaxNode};
|
||||
|
||||
pub const CURSOR_MARKER: &str = "$0";
|
||||
|
||||
@ -30,7 +30,12 @@ impl TestDB {
|
||||
(this, root_id, pos)
|
||||
}
|
||||
|
||||
pub fn node_at<N: AstNode<Language = NixLanguage>>(&self, file_id: FileId, pos: TextSize) -> N {
|
||||
pub fn find_node<T>(
|
||||
&self,
|
||||
file_id: FileId,
|
||||
pos: TextSize,
|
||||
f: impl FnMut(SyntaxNode) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
self.parse(file_id)
|
||||
.value
|
||||
.syntax_node()
|
||||
@ -38,7 +43,11 @@ impl TestDB {
|
||||
.right_biased()
|
||||
.expect("No token")
|
||||
.parent_ancestors()
|
||||
.find_map(N::cast)
|
||||
.find_map(f)
|
||||
}
|
||||
|
||||
pub fn node_at<N: AstNode<Language = NixLanguage>>(&self, file_id: FileId, pos: TextSize) -> N {
|
||||
self.find_node(file_id, pos, N::cast)
|
||||
.expect("No node found")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user