feat: resolve module references in import at syntax level (#933)

This commit is contained in:
Myriad-Dreamin 2024-12-03 16:15:48 +08:00 committed by GitHub
parent 672a95c908
commit b38e9bdf0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 84 additions and 13 deletions

View file

@ -0,0 +1,13 @@
/// path: draw.typ
/// The draw line.
#let line() = 1;
-----
/// path: lib.typ
#import "draw.typ"
-----
#import "lib.typ"
#import lib.draw: *
#(/* position after */ line);

View file

@ -0,0 +1,10 @@
---
source: crates/tinymist-query/src/hover.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/module_star_shadow.typ
snapshot_kind: text
---
{
"contents": "```typc\nlet line() = int;\n```\n\n---\nThe draw line.",
"range": "2:23:2:27"
}

View file

@ -591,10 +591,10 @@ impl ExprWorker<'_> {
fn check_module_import(&mut self, typed: ast::ModuleImport) -> Expr {
let source = typed.source();
crate::log_debug_ct!("checking import: {source:?}");
let mod_expr = self.check_import(typed.source(), true);
crate::log_debug_ct!("checking import: {source:?} => {mod_expr:?}");
let decl = typed.new_name().map(Decl::module_alias).or_else(|| {
let mod_var = typed.new_name().map(Decl::module_alias).or_else(|| {
typed.imports().is_none().then(|| {
let name = match mod_expr.as_ref()? {
Expr::Decl(d) if matches!(d.as_ref(), Decl::Module { .. }) => d.name().clone(),
@ -605,19 +605,19 @@ impl ExprWorker<'_> {
})?
});
let is_named = decl.is_some();
let decl = Interned::new(decl.unwrap_or_else(|| Decl::module_import(typed.span())));
let creating_mod_var = mod_var.is_some();
let mod_var = Interned::new(mod_var.unwrap_or_else(|| Decl::module_import(typed.span())));
let mod_ref = RefExpr {
decl: decl.clone(),
decl: mod_var.clone(),
step: mod_expr.clone(),
root: mod_expr.clone(),
val: None,
};
crate::log_debug_ct!("create import variable: {mod_ref:?}");
let mod_ref = Interned::new(mod_ref);
if is_named {
if creating_mod_var {
self.scope_mut()
.insert_mut(decl.name().clone(), Expr::Ref(mod_ref.clone()));
.insert_mut(mod_var.name().clone(), Expr::Ref(mod_ref.clone()));
}
self.resolve_as(mod_ref.clone());
@ -666,7 +666,7 @@ impl ExprWorker<'_> {
let scope = if let Some(scope) = scope {
scope
} else {
crate::log_debug_ct!(
log::warn!(
"cannot analyze import on: {typed:?}, expr {mod_expr:?}, in file {:?}",
typed.span().id()
);
@ -680,7 +680,7 @@ impl ExprWorker<'_> {
self.lexical.scopes.push(scope);
}
ast::Imports::Items(items) => {
let module = Expr::Decl(decl.clone());
let module = Expr::Decl(mod_var.clone());
self.import_decls(&scope, module, items);
}
}
@ -702,7 +702,17 @@ impl ExprWorker<'_> {
})?;
crate::log_debug_ct!("checking import source: {src_expr:?}");
let src_str = match &src_expr {
self.check_import_by_str(source, &src_expr, is_import)
.or_else(|| self.check_import_by_def(&src_expr))
}
fn check_import_by_str(
&mut self,
source: ast::Expr,
src_expr: &Expr,
is_import: bool,
) -> Option<Expr> {
let src_str = match src_expr {
Expr::Type(Ty::Value(val)) => {
if val.val.scope().is_some() {
return Some(src_expr.clone());
@ -740,6 +750,14 @@ impl ExprWorker<'_> {
Some(module)
}
fn check_import_by_def(&mut self, src_expr: &Expr) -> Option<Expr> {
match src_expr {
Expr::Decl(..) => Some(src_expr.clone()),
Expr::Ref(r) => r.root.clone(),
_ => None,
}
}
fn import_decls(&mut self, scope: &ExprScope, module: Expr, items: ast::ImportItems) {
crate::log_debug_ct!("import scope {scope:?}");
@ -1034,7 +1052,10 @@ impl ExprWorker<'_> {
}
fn resolve_as(&mut self, r: Interned<RefExpr>) {
let s = r.decl.span();
self.resolve_as_(r.decl.span(), r);
}
fn resolve_as_(&mut self, s: Span, r: Interned<RefExpr>) {
self.buffer.push((s, r.clone()));
}
@ -1137,7 +1158,7 @@ impl ExprWorker<'_> {
(None, val)
}
fn fold_expr_and_val(&self, src: ConcolicExpr) -> Option<Expr> {
fn fold_expr_and_val(&mut self, src: ConcolicExpr) -> Option<Expr> {
crate::log_debug_ct!("folding cc: {src:?}");
match src {
(None, Some(val)) => Some(Expr::Type(val)),
@ -1145,7 +1166,7 @@ impl ExprWorker<'_> {
}
}
fn fold_expr(&self, src: Option<Expr>) -> Option<Expr> {
fn fold_expr(&mut self, src: Option<Expr>) -> Option<Expr> {
crate::log_debug_ct!("folding cc: {src:?}");
match src {
Some(Expr::Decl(decl)) if !decl.is_def() => {
@ -1157,6 +1178,11 @@ impl ExprWorker<'_> {
crate::log_debug_ct!("folding ref: {r:?}");
self.fold_expr_and_val((r.root.clone(), r.val.clone()))
}
Some(Expr::Select(r)) => {
let lhs = self.fold_expr(Some(r.lhs.clone()));
crate::log_debug_ct!("folding select: {r:?} ([{lhs:?}].[{:?}])", r.key);
self.syntax_level_select(lhs?, &r.key, r.span)
}
Some(expr) => {
crate::log_debug_ct!("folding expr: {expr:?}");
Some(expr)
@ -1165,6 +1191,28 @@ impl ExprWorker<'_> {
}
}
fn syntax_level_select(&mut self, lhs: Expr, key: &Interned<Decl>, span: Span) -> Option<Expr> {
match &lhs {
Expr::Decl(d) => match d.as_ref() {
Decl::Module(m) => {
let f = self.exports_of(m.fid);
let selected = f.get(key.name())?;
let select_ref = Interned::new(RefExpr {
decl: key.clone(),
root: Some(lhs.clone()),
step: Some(selected.clone()),
val: None,
});
self.resolve_as(select_ref.clone());
self.resolve_as_(span, select_ref);
Some(selected.clone())
}
_ => None,
},
_ => None,
}
}
fn exports_of(&mut self, fid: TypstFileId) -> LexicalScope {
let imported = self
.ctx