diff --git a/crates/tinymist-query/src/analysis/post_tyck.rs b/crates/tinymist-query/src/analysis/post_tyck.rs index 38ca8dc9..d2afaf69 100644 --- a/crates/tinymist-query/src/analysis/post_tyck.rs +++ b/crates/tinymist-query/src/analysis/post_tyck.rs @@ -94,7 +94,7 @@ fn check_signature<'a>( pub(crate) struct PostTypeChecker<'a, 'w> { ctx: &'a mut AnalysisContext<'w>, - info: &'a TypeScheme, + pub info: &'a TypeScheme, checked: HashMap>, locals: TypeScheme, } diff --git a/crates/tinymist-query/src/analysis/signature.rs b/crates/tinymist-query/src/analysis/signature.rs index 3cd5fc53..6477c71e 100644 --- a/crates/tinymist-query/src/analysis/signature.rs +++ b/crates/tinymist-query/src/analysis/signature.rs @@ -1,11 +1,15 @@ //! Analysis of function signatures. -use typst::foundations::{self, Closure, ParamInfo}; +use itertools::Either; +use tinymist_derive::BindTyCtx; +use typst::foundations::{Closure, ParamInfo}; -use super::{prelude::*, resolve_callee, BuiltinTy, SigTy, TypeSources}; +use super::{prelude::*, resolve_callee, BoundChecker, DocSource, SigTy, TypeVar}; use crate::analysis::PostTypeChecker; -use crate::docs::UntypedSymbolDocs; +use crate::docs::{UntypedSignatureDocs, UntypedSymbolDocs, UntypedVarDocs}; use crate::syntax::get_non_strict_def_target; +use crate::ty::TyCtx; +use crate::ty::TypeBounds; use crate::upstream::truncated_repr; /// Describes a function parameter. @@ -259,29 +263,45 @@ fn analyze_type_signature( let node = source.find(f.span())?; let def = get_non_strict_def_target(node.parent()?.clone())?; let type_info = ctx.type_check(&source)?; - let ty = type_info.type_of_span(def.node().span())?; + let ty = type_info.type_of_span(def.name()?.span())?; Some((type_info, ty)) } }?; - log::debug!("check type signature of ty: {ty:?}"); // todo multiple sources let mut srcs = ty.sources(); srcs.sort(); + log::debug!("check type signature of ty: {ty:?} => {srcs:?}"); let type_var = srcs.into_iter().next()?; match type_var { - TypeSources::Var(v) => { - let sig_ty = ty.sig_repr(true, &mut PostTypeChecker::new(ctx, &type_info))?; + DocSource::Var(v) => { + let mut ty_ctx = PostTypeChecker::new(ctx, &type_info); + let sig_ty = Ty::Func(ty.sig_repr(true, &mut ty_ctx)?); + let sig_ty = type_info.simplify(sig_ty, false); + let Ty::Func(sig_ty) = sig_ty else { + panic!("expected function type, got {sig_ty:?}"); + }; - let docstring = match type_info.var_docs.get(&v.def).map(|x| x.as_ref()) { - Some(UntypedSymbolDocs::Function(sig)) => sig, + // todo: this will affect inlay hint: _var_with + let (_var_with, docstring) = match type_info.var_docs.get(&v.def).map(|x| x.as_ref()) { + Some(UntypedSymbolDocs::Function(sig)) => (vec![], Either::Left(sig.as_ref())), + Some(UntypedSymbolDocs::Variable(d)) => find_alias_stack(&mut ty_ctx, &v, d)?, _ => return None, }; + let docstring = match docstring { + Either::Left(docstring) => docstring, + Either::Right(f) => return Some(ctx.type_of_func(f)), + }; + let mut param_specs = Vec::new(); let mut has_fill_or_size_or_stroke = false; let mut _broken = false; + if docstring.pos.len() != sig_ty.positional_params().len() { + panic!("positional params mismatch: {docstring:#?} != {sig_ty:#?}"); + } + for (doc, ty) in docstring.pos.iter().zip(sig_ty.positional_params()) { let default = doc.default.clone(); let ty = ty.clone(); @@ -338,23 +358,99 @@ fn analyze_type_signature( _broken, }))) } - TypeSources::Builtin(BuiltinTy::Type(ty)) => { - let cons = ty.constructor().ok()?; - Some(ctx.type_of_func(cons)) + src @ (DocSource::Builtin(..) | DocSource::Ins(..)) => { + Some(ctx.type_of_func(src.as_func()?)) } - TypeSources::Builtin(BuiltinTy::Element(ty)) => { - let cons: Func = ty.into(); - Some(ctx.type_of_func(cons)) + } +} + +fn find_alias_stack<'a>( + ctx: &'a mut PostTypeChecker, + v: &Interned, + d: &'a UntypedVarDocs, +) -> Option<( + Vec<&'a UntypedVarDocs>, + Either<&'a UntypedSignatureDocs, Func>, +)> { + let mut checker = AliasStackChecker { + ctx, + stack: vec![d], + res: None, + checking_with: true, + }; + Ty::Var(v.clone()).bounds(true, &mut checker); + + checker.res.map(|res| (checker.stack, res)) +} + +#[derive(BindTyCtx)] +#[bind(ctx)] +struct AliasStackChecker<'a, 'b, 'w> { + ctx: &'a mut PostTypeChecker<'b, 'w>, + stack: Vec<&'a UntypedVarDocs>, + res: Option>, + checking_with: bool, +} + +impl<'a, 'b, 'w> BoundChecker for AliasStackChecker<'a, 'b, 'w> { + fn check_var(&mut self, u: &Interned, pol: bool) { + log::debug!("collecting var {u:?} {pol:?}"); + if self.res.is_some() { + return; } - TypeSources::Builtin(..) => None, - TypeSources::Ins(i) => match &i.val { - foundations::Value::Func(f) => Some(ctx.type_of_func(f.clone())), - foundations::Value::Type(f) => { - let cons = f.constructor().ok()?; - Some(ctx.type_of_func(cons)) + + if self.checking_with { + self.check_var_rec(u, pol); + return; + } + + let docs = self.ctx.info.var_docs.get(&u.def).map(|x| x.as_ref()); + + log::debug!("collecting var {u:?} {pol:?} => {docs:?}"); + // todo: bind builtin functions + match docs { + Some(UntypedSymbolDocs::Function(sig)) => { + self.res = Some(Either::Left(sig)); } - _ => None, - }, + Some(UntypedSymbolDocs::Variable(d)) => { + self.checking_with = true; + self.stack.push(d); + self.check_var_rec(u, pol); + self.stack.pop(); + self.checking_with = false; + } + _ => {} + } + } + + fn collect(&mut self, ty: &Ty, pol: bool) { + if self.res.is_some() { + return; + } + + match (self.checking_with, ty) { + (true, Ty::With(w)) => { + log::debug!("collecting with {ty:?} {pol:?}"); + self.checking_with = false; + w.sig.bounds(pol, self); + self.checking_with = true; + } + (false, ty) => { + if let Some(src) = ty.as_source() { + match src { + DocSource::Var(u) => { + self.check_var(&u, pol); + } + src @ (DocSource::Builtin(..) | DocSource::Ins(..)) => { + if let Some(f) = src.as_func() { + self.res = Some(Either::Right(f)); + } + } + } + } + } + _ => {} + } } } diff --git a/crates/tinymist-query/src/analysis/tyck/apply.rs b/crates/tinymist-query/src/analysis/tyck/apply.rs index 9189f16b..b5957ced 100644 --- a/crates/tinymist-query/src/analysis/tyck/apply.rs +++ b/crates/tinymist-query/src/analysis/tyck/apply.rs @@ -10,6 +10,7 @@ use crate::{analysis::ApplyChecker, ty::ArgsTy}; pub struct ApplyTypeChecker<'a, 'b, 'w> { pub(super) base: &'a mut TypeChecker<'b, 'w>, pub call_site: Span, + pub call_raw_for_with: Option, pub args: Option>, pub resultant: Vec, } @@ -59,6 +60,7 @@ impl<'a, 'b, 'w> ApplyChecker for ApplyTypeChecker<'a, 'b, 'w> { base, call_site: Span::detached(), args: None, + call_raw_for_with: None, resultant: vec![], }; p0.call(&args, true, &mut mapper); @@ -74,6 +76,7 @@ impl<'a, 'b, 'w> ApplyChecker for ApplyTypeChecker<'a, 'b, 'w> { base, call_site: Span::detached(), args: None, + call_raw_for_with: None, resultant: vec![], }; p0.call(&args, true, &mut mapper); @@ -153,36 +156,14 @@ impl<'a, 'b, 'w> ApplyChecker for ApplyTypeChecker<'a, 'b, 'w> { } if is_partialize { - let Some(sig) = callee else { - log::warn!("Partialize is not implemented yet {sig:?}"); - return; - }; - self.resultant - .push(Ty::With(SigWithTy::new(sig.into(), args.clone()))); + log::debug!("Partialize location {sig:?} a.k.a {callee:?}"); + if let Some(Ty::Select(call_raw_for_with)) = self.call_raw_for_with.take() { + self.resultant.push(Ty::With(SigWithTy::new( + call_raw_for_with.ty.clone(), + args.clone(), + ))); + } } - - // let f = v.as_ref(); - // let mut pos = f.pos.iter(); - // // let mut named = f.named.clone(); - // // let mut rest = f.rest.clone(); - - // for pos_in in args.start_match() { - // let pos_ty = pos.next().unwrap_or(&FlowType::Any); - // self.constrain(pos_in, pos_ty); - // } - - // for (name, named_in) in &args.named { - // let named_ty = f.named.iter().find(|(n, _)| n == - // name).map(|(_, ty)| ty); if let Some(named_ty) = - // named_ty { self.constrain(named_in, - // named_ty); } - // }' - - // todo: hold signature - // self.info.witness_at_least( - // callee_span, - // FlowType::Value(TypeIns::new(Value::Func(f.clone()))), - // ); } } diff --git a/crates/tinymist-query/src/analysis/tyck/docs.rs b/crates/tinymist-query/src/analysis/tyck/docs.rs index 87ae43be..07af6605 100644 --- a/crates/tinymist-query/src/analysis/tyck/docs.rs +++ b/crates/tinymist-query/src/analysis/tyck/docs.rs @@ -103,8 +103,6 @@ pub struct VarDoc { pub docs: Option, /// The type of the variable pub ty: Option, - /// The default value of the variable - pub default: Option, } pub(crate) fn compute_docstring( @@ -154,7 +152,6 @@ impl<'a, 'w> DocsChecker<'a, 'w> { VarDoc { docs: Some(param.docs), ty: self.check_type_strings(&module, ¶m.types), - default: param.default, }, ); } @@ -178,7 +175,7 @@ impl<'a, 'w> DocsChecker<'a, 'w> { let res_ty = converted .return_ty - .and_then(|ty| self.check_type_strings(&module, &ty)); + .and_then(|ty| self.check_type_strings(&module, &ty.0)); Some(DocString { docs: Some(converted.docs), diff --git a/crates/tinymist-query/src/analysis/tyck/syntax.rs b/crates/tinymist-query/src/analysis/tyck/syntax.rs index cfbcd8e9..68c00e7b 100644 --- a/crates/tinymist-query/src/analysis/tyck/syntax.rs +++ b/crates/tinymist-query/src/analysis/tyck/syntax.rs @@ -2,7 +2,9 @@ use super::*; use crate::analysis::ParamAttrs; -use crate::docs::{DocStringKind, SignatureDocsT, TypelessParamDocs, UntypedSymbolDocs}; +use crate::docs::{ + DocStringKind, SignatureDocsT, TidyVarDocsT, TypelessParamDocs, UntypedSymbolDocs, +}; use crate::ty::*; static EMPTY_DOCSTRING: LazyLock = LazyLock::new(DocString::default); @@ -331,6 +333,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> { base: self, call_site: func_call.callee().span(), args: Some(func_call.args()), + call_raw_for_with: Some(callee.clone()), resultant: vec![], }; callee.call(&args, true, &mut worker); @@ -397,7 +400,15 @@ impl<'a, 'w> TypeChecker<'a, 'w> { name, docs: param_doc.docs.clone().unwrap_or_default(), cano_type: (), - default: param_doc.default.clone(), + default: None, + attrs: ParamAttrs::positional(), + }); + } else { + pos_docs.push(TypelessParamDocs { + name: "_".into(), + docs: Default::default(), + cano_type: (), + default: None, attrs: ParamAttrs::positional(), }); } @@ -422,7 +433,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> { name: name.clone(), docs: param_doc.docs.clone().unwrap_or_default(), cano_type: (), - default: param_doc.default.clone(), + default: Some(e.expr().to_untyped().clone().into_text()), attrs: ParamAttrs::named(), }, ); @@ -445,7 +456,16 @@ impl<'a, 'w> TypeChecker<'a, 'w> { name: e.get().as_str().into(), docs: param_doc.docs.clone().unwrap_or_default(), cano_type: (), - default: param_doc.default.clone(), + default: None, + attrs: ParamAttrs::variadic(), + }); + } else { + rest = Some(Ty::Builtin(BuiltinTy::Args)); + rest_docs = Some(TypelessParamDocs { + name: "_".into(), + docs: Default::default(), + cano_type: (), + default: None, attrs: ParamAttrs::variadic(), }); } @@ -532,6 +552,21 @@ impl<'a, 'w> TypeChecker<'a, 'w> { let value = docstring.res_ty.clone().unwrap_or(value); self.check_pattern(pattern, value, docstring, root.clone()); + + if let ast::Pattern::Normal(ast::Expr::Ident(ident)) = pattern { + let def_id = Some(ident) + .and_then(|n| self.get_def_id(n.span(), &to_ident_ref(&root, n)?)); + + if let Some(def_id) = def_id { + self.info.var_docs.insert( + def_id, + Arc::new(UntypedSymbolDocs::Variable(TidyVarDocsT { + docs: docstring.docs.clone().unwrap_or_default(), + return_ty: (), + })), + ); + } + } } } @@ -555,6 +590,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> { base: self, call_site: set_rule.target().span(), args: Some(set_rule.args()), + call_raw_for_with: Some(callee.clone()), resultant: vec![], }; callee.call(&args, true, &mut worker); diff --git a/crates/tinymist-query/src/docs/symbol.rs b/crates/tinymist-query/src/docs/symbol.rs index 21d8bbc8..ef95226c 100644 --- a/crates/tinymist-query/src/docs/symbol.rs +++ b/crates/tinymist-query/src/docs/symbol.rs @@ -68,7 +68,7 @@ pub enum SymbolDocsT { Function(Box>), /// Documentation about a variable. #[serde(rename = "var")] - Variable(TidyVarDocs), + Variable(TidyVarDocsT), /// Documentation about a module. #[serde(rename = "module")] Module(TidyModuleDocs), @@ -139,6 +139,8 @@ pub struct SignatureDocsT { pub ret_ty: T, } +/// Documentation about a signature. +pub type UntypedSignatureDocs = SignatureDocsT<()>; /// Documentation about a signature. pub type SignatureDocs = SignatureDocsT; diff --git a/crates/tinymist-query/src/docs/tidy.rs b/crates/tinymist-query/src/docs/tidy.rs index f4a578fe..2897aac8 100644 --- a/crates/tinymist-query/src/docs/tidy.rs +++ b/crates/tinymist-query/src/docs/tidy.rs @@ -18,10 +18,15 @@ pub struct TidyFuncDocs { pub params: Vec, } +/// Documentation about a variable (without type information). +pub type UntypedVarDocs = TidyVarDocsT<()>; +/// Documentation about a variable. +pub type TidyVarDocs = TidyVarDocsT>; + #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TidyVarDocs { +pub struct TidyVarDocsT { pub docs: EcoString, - pub return_ty: Option, + pub return_ty: T, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -164,7 +169,8 @@ pub fn identify_var_docs(converted: EcoString) -> StrResult { break; }; - return_ty = Some(w.trim().to_string()); + // todo: convert me + return_ty = Some((w.trim().into(), String::new())); break_line = Some(i); break; } @@ -233,7 +239,7 @@ mod tests { let f = super::identify_var_docs(s.into()).unwrap(); let mut res = format!(">> docs:\n{}\n<< docs", f.docs); if let Some(t) = f.return_ty { - res.push_str(&format!("\n>>return\n{t}\n<>return\n{}\n< +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "x" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with2.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with2.typ.snap index 894f8535..588fd9ff 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with2.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_named_with2.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/user_named_with2.typ --- - +y: 1 -> CallParamInfo { kind: Named, is_content_block: false, param_name: "y" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with.typ.snap index ab9b193f..4862953c 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/user_with.typ --- - +1 -> CallParamInfo { kind: Positional, is_content_block: false, param_name: "x" } diff --git a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with2.typ.snap b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with2.typ.snap index 948411b5..976ebbfd 100644 --- a/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with2.typ.snap +++ b/crates/tinymist-query/src/fixtures/call_info/snaps/test@user_with2.typ.snap @@ -3,4 +3,4 @@ source: crates/tinymist-query/src/analysis.rs expression: CallSnapshot(result.as_deref()) input_file: crates/tinymist-query/src/fixtures/call_info/user_with2.typ --- - + diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@import.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@import.typ.snap index 8846b005..1a85d753 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@import.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@import.typ.snap @@ -12,7 +12,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import.typ "kind": 3, "label": "aab", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aab(${1:})", @@ -32,7 +32,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import.typ "kind": 3, "label": "aac", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aac(${1:})", @@ -57,7 +57,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import.typ "kind": 3, "label": "aac", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aac(${1:})", diff --git a/crates/tinymist-query/src/fixtures/completion/snaps/test@import_star.typ.snap b/crates/tinymist-query/src/fixtures/completion/snaps/test@import_star.typ.snap index 391812f8..48cb74e1 100644 --- a/crates/tinymist-query/src/fixtures/completion/snaps/test@import_star.typ.snap +++ b/crates/tinymist-query/src/fixtures/completion/snaps/test@import_star.typ.snap @@ -12,7 +12,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import_star.typ "kind": 3, "label": "aa", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aa(${1:})", @@ -32,7 +32,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import_star.typ "kind": 3, "label": "aa.with", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aa.with(${1:})", @@ -52,7 +52,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import_star.typ "kind": 3, "label": "aab", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aab(${1:})", @@ -72,7 +72,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import_star.typ "kind": 3, "label": "aabc", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aabc(${1:})", @@ -92,7 +92,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import_star.typ "kind": 3, "label": "aac", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aac(${1:})", @@ -117,7 +117,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import_star.typ "kind": 3, "label": "aabc", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aabc(${1:})", @@ -137,7 +137,7 @@ input_file: crates/tinymist-query/src/fixtures/completion/import_star.typ "kind": 3, "label": "aac", "labelDetails": { - "description": "() => any" + "description": "() => 1" }, "textEdit": { "newText": "aac(${1:})", diff --git a/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_fn.typ.snap b/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_fn.typ.snap index 16ea49b2..4da70af6 100644 --- a/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_fn.typ.snap +++ b/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_fn.typ.snap @@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)" input_file: crates/tinymist-query/src/fixtures/hover/annotate_fn.typ --- { - "contents": "```typc\nlet touying-fn-wrapper(fn, ..args, max-repetitions = none, repetitions = none);\n```\n\n---\n\n\n #let fn = `(..fn-args) => any`;\n\n - fn (function, fn): The `fn`.\n - max-repetitions (int): The `max-repetitions`.\n - repetitions (int): The `repetitions`.\n - args (any, fn-args): The `args`.", + "contents": "```typc\nlet touying-fn-wrapper(fn: (..: []) => any | function, ..args: arguments, max-repetitions: int | none = none, repetitions: int | none = none);\n```\n\n---\n\n\n #let fn = `(..fn-args) => any`;\n\n - fn (function, fn): The `fn`.\n - max-repetitions (int): The `max-repetitions`.\n - repetitions (int): The `repetitions`.\n - args (any, fn-args): The `args`.", "range": "8:20:8:38" } diff --git a/crates/tinymist-query/src/fixtures/signature/snaps/test@builtin_with.typ.snap b/crates/tinymist-query/src/fixtures/signature/snaps/test@builtin_with.typ.snap index 5de70785..53f1b6f9 100644 --- a/crates/tinymist-query/src/fixtures/signature/snaps/test@builtin_with.typ.snap +++ b/crates/tinymist-query/src/fixtures/signature/snaps/test@builtin_with.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/analysis.rs expression: SignatureSnapshot(result.as_ref()) input_file: crates/tinymist-query/src/fixtures/signature/builtin_with.typ --- -with 50%, 50%, 50%, fn( red, green, diff --git a/crates/tinymist-query/src/fixtures/signature/snaps/test@user_with.typ.snap b/crates/tinymist-query/src/fixtures/signature/snaps/test@user_with.typ.snap index a371c0f2..f212a006 100644 --- a/crates/tinymist-query/src/fixtures/signature/snaps/test@user_with.typ.snap +++ b/crates/tinymist-query/src/fixtures/signature/snaps/test@user_with.typ.snap @@ -3,7 +3,6 @@ source: crates/tinymist-query/src/analysis.rs expression: SignatureSnapshot(result.as_ref()) input_file: crates/tinymist-query/src/fixtures/signature/user_with.typ --- -with 1, fn( u, v, diff --git a/crates/tinymist-query/src/fixtures/type_check/snaps/test@with.typ.snap b/crates/tinymist-query/src/fixtures/type_check/snaps/test@with.typ.snap index 6e393241..c1efa764 100644 --- a/crates/tinymist-query/src/fixtures/type_check/snaps/test@with.typ.snap +++ b/crates/tinymist-query/src/fixtures/type_check/snaps/test@with.typ.snap @@ -14,7 +14,7 @@ input_file: crates/tinymist-query/src/fixtures/type_check/with.typ 20..21 -> @g 24..25 -> @f 24..30 -> (@x) => @x -24..33 -> ((@x) => @x).with(..(1) => any) +24..33 -> (@f).with(..(1) => any) 40..41 -> @x 44..45 -> (Any | @g) 44..47 -> 1 diff --git a/crates/tinymist-query/src/syntax/matcher.rs b/crates/tinymist-query/src/syntax/matcher.rs index b9f60257..c50c908a 100644 --- a/crates/tinymist-query/src/syntax/matcher.rs +++ b/crates/tinymist-query/src/syntax/matcher.rs @@ -243,6 +243,10 @@ impl<'a> DefTarget<'a> { } pub fn name_range(&self) -> Option> { + self.name().map(|node| node.range()) + } + + pub fn name(&self) -> Option { match self { DefTarget::Let(node) => { let lb: ast::LetBinding<'_> = node.cast()?; @@ -254,7 +258,7 @@ impl<'a> DefTarget<'a> { _ => return None, }; - Some(names.range()) + Some(names) } DefTarget::Import(_node) => { // let ident = node.cast::()?; diff --git a/crates/tinymist-query/src/ty/apply.rs b/crates/tinymist-query/src/ty/apply.rs index 285c87f5..18075cf7 100644 --- a/crates/tinymist-query/src/ty/apply.rs +++ b/crates/tinymist-query/src/ty/apply.rs @@ -38,6 +38,10 @@ impl<'a, T: ApplyChecker> ApplySigChecker<'a, T> { impl<'a, T: ApplyChecker> SigChecker for ApplySigChecker<'a, T> { fn check(&mut self, cano_sig: Sig, ctx: &mut super::SigCheckContext, pol: bool) -> Option<()> { + let (cano_sig, is_partialize) = match cano_sig { + Sig::Partialize(sig) => (*sig, true), + sig => (sig, false), + }; // Bind the arguments to the canonical signature. let partial_sig = if ctx.args.is_empty() { cano_sig @@ -48,6 +52,12 @@ impl<'a, T: ApplyChecker> SigChecker for ApplySigChecker<'a, T> { at: &ctx.at, } }; + let partial_sig = if is_partialize { + Sig::Partialize(&partial_sig) + } else { + partial_sig + }; + self.0.apply(partial_sig, self.1, pol); Some(()) } diff --git a/crates/tinymist-query/src/ty/bound.rs b/crates/tinymist-query/src/ty/bound.rs index cf61dae0..5ef80f15 100644 --- a/crates/tinymist-query/src/ty/bound.rs +++ b/crates/tinymist-query/src/ty/bound.rs @@ -1,31 +1,81 @@ -use typst::foundations; +use typst::foundations::{self, Func}; use crate::ty::prelude::*; -pub trait BoundChecker: TyCtx { +pub trait BoundChecker: Sized + TyCtx { fn collect(&mut self, ty: &Ty, pol: bool); + + fn check_var(&mut self, u: &Interned, pol: bool) { + self.check_var_rec(u, pol); + } + + fn check_var_rec(&mut self, u: &Interned, pol: bool) { + let Some(w) = self.global_bounds(u, pol) else { + return; + }; + let mut ctx = BoundCheckContext; + ctx.tys(w.ubs.iter(), pol, self); + ctx.tys(w.lbs.iter(), !pol, self); + } } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum TypeSources { +pub enum DocSource { Var(Interned), Ins(Interned), Builtin(BuiltinTy), } +impl DocSource { + /// Regard doc source as function. + pub fn as_func(&self) -> Option { + match self { + Self::Var(..) => None, + Self::Builtin(BuiltinTy::Type(ty)) => Some(ty.constructor().ok()?), + Self::Builtin(BuiltinTy::Element(ty)) => Some((*ty).into()), + Self::Builtin(..) => None, + Self::Ins(i) => match &i.val { + foundations::Value::Func(f) => Some(f.clone()), + foundations::Value::Type(f) => Some(f.constructor().ok()?), + _ => None, + }, + } + } +} + impl Ty { /// Check if the given type has bounds (is combinated). pub fn has_bounds(&self) -> bool { matches!(self, Ty::Union(_) | Ty::Let(_) | Ty::Var(_)) } + /// Convert type to doc source + pub fn as_source(&self) -> Option { + match self { + Ty::Builtin(ty @ (BuiltinTy::Type(..) | BuiltinTy::Element(..))) => { + Some(DocSource::Builtin(ty.clone())) + } + Ty::Value(ty) => match &ty.val { + foundations::Value::Type(..) | foundations::Value::Func(..) => { + Some(DocSource::Ins(ty.clone())) + } + _ => None, + }, + _ => None, + } + } + /// Get the sources of the given type. - pub fn sources(&self) -> Vec { + pub fn sources(&self) -> Vec { let mut results = vec![]; - fn collect(ty: &Ty, results: &mut Vec) { + fn collect(ty: &Ty, results: &mut Vec) { use Ty::*; + if let Some(src) = ty.as_source() { + results.push(src); + return; + } match ty { - Any | Boolean(_) | If(..) => {} + Any | Boolean(_) | If(..) | Builtin(..) | Value(..) => {} Dict(..) | Array(..) | Tuple(..) | Func(..) | Args(..) => {} Unary(..) | Binary(..) => {} Field(ty) => { @@ -45,18 +95,8 @@ impl Ty { } } Var(ty) => { - results.push(TypeSources::Var(ty.clone())); + results.push(DocSource::Var(ty.clone())); } - Builtin(ty @ (BuiltinTy::Type(..) | BuiltinTy::Element(..))) => { - results.push(TypeSources::Builtin(ty.clone())); - } - Builtin(..) => {} - Value(ty) => match &ty.val { - foundations::Value::Type(..) | foundations::Value::Func(..) => { - results.push(TypeSources::Ins(ty.clone())); - } - _ => {} - }, With(ty) => collect(&ty.sig, results), Select(ty) => collect(&ty.ty, results), } @@ -90,13 +130,7 @@ impl BoundCheckContext { self.tys(u.ubs.iter(), pol, checker); self.tys(u.lbs.iter(), !pol, checker); } - Ty::Var(u) => { - let Some(w) = checker.global_bounds(u, pol) else { - return; - }; - self.tys(w.ubs.iter(), pol, checker); - self.tys(w.lbs.iter(), !pol, checker); - } + Ty::Var(u) => checker.check_var(u, pol), // todo: calculate these operators // Ty::Select(_) => {} // Ty::Unary(_) => {} diff --git a/crates/tinymist-query/src/ty/sig.rs b/crates/tinymist-query/src/ty/sig.rs index 8c8aef46..d0225b31 100644 --- a/crates/tinymist-query/src/ty/sig.rs +++ b/crates/tinymist-query/src/ty/sig.rs @@ -112,7 +112,6 @@ impl Ty { impl SigChecker for SigReprDriver<'_, C> { fn check(&mut self, sig: Sig, _ctx: &mut SigCheckContext, _pol: bool) -> Option<()> { - // todo: bind type context let sig = sig.shape(self.0)?; *self.1 = Some(sig.sig.clone()); Some(()) @@ -316,6 +315,11 @@ impl<'a, 'b> BoundChecker for MethodDriver<'a, 'b> { // todo: general select operator } } + Ty::With(w) => { + self.0.ctx.args.push(w.with.clone()); + w.sig.bounds(pol, self); + self.0.ctx.args.pop(); + } Ty::Tuple(..) => self.array_method(ty, pol), Ty::Array(..) => self.array_method(ty, pol), // todo: general select operator diff --git a/tests/e2e/main.rs b/tests/e2e/main.rs index 32fb8f8f..26708afb 100644 --- a/tests/e2e/main.rs +++ b/tests/e2e/main.rs @@ -385,7 +385,7 @@ fn e2e() { }); let hash = replay_log(&tinymist_binary, &root.join("vscode")); - insta::assert_snapshot!(hash, @"siphash128_13:93099dba5af1df251bc257e227a2ee2"); + insta::assert_snapshot!(hash, @"siphash128_13:3d7342fcd50a7d88cc19938b3d814dc4"); } }