feat: type check labels with tolerating syntax error (#975)

* feat: type check labels with tolerating syntax error

* fix: naming

* fix: use and type
This commit is contained in:
Myriad-Dreamin 2024-12-10 18:49:06 +08:00 committed by GitHub
parent 2f882378bc
commit c430e3d051
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 93 additions and 33 deletions

View file

@ -189,19 +189,11 @@ impl<'a> PostTypeChecker<'a> {
node.kind(),
);
Ty::or(self_ty, contextual_self_ty)
Ty::union(self_ty, contextual_self_ty)
}
fn check_context_or(&mut self, context: &LinkedNode, context_ty: Option<Ty>) -> Option<Ty> {
let checked_context = self.check(context);
if checked_context.is_some() && context_ty.is_some() {
let c = checked_context?;
let s = context_ty?;
Some(Ty::from_types([c, s].into_iter()))
} else {
checked_context.or(context_ty)
}
fn check_or(&mut self, node: &LinkedNode, ty: Option<Ty>) -> Option<Ty> {
Ty::union(self.check(node), ty)
}
fn check_target(&mut self, node: Option<CheckTarget>, context_ty: Option<Ty>) -> Option<Ty> {
@ -210,14 +202,14 @@ impl<'a> PostTypeChecker<'a> {
};
crate::log_debug_ct!("post check target: {node:?}");
match node {
match &node {
CheckTarget::Param {
callee,
args: _,
target,
is_set,
} => {
let callee = self.check_context_or(&callee, context_ty)?;
let callee = self.check_or(callee, context_ty)?;
crate::log_debug_ct!(
"post check call target: ({callee:?})::{target:?} is_set: {is_set}"
);
@ -226,7 +218,7 @@ impl<'a> PostTypeChecker<'a> {
crate::log_debug_ct!("post check call sig: {target:?} {sig:?}");
let mut resp = SignatureReceiver::default();
match &target {
match target {
ParamTarget::Named(n) => {
let ident = n.cast::<ast::Ident>()?.into();
let ty = sig.primary().get_named(&ident)?;
@ -255,7 +247,7 @@ impl<'a> PostTypeChecker<'a> {
// names
for field in sig.primary().named() {
if is_set && !field.attrs.settable {
if *is_set && !field.attrs.settable {
continue;
}
@ -268,7 +260,7 @@ impl<'a> PostTypeChecker<'a> {
Some(resp.finalize())
}
CheckTarget::Element { container, target } => {
let container_ty = self.check_context_or(&container, context_ty)?;
let container_ty = self.check_or(container, context_ty)?;
crate::log_debug_ct!("post check element target: ({container_ty:?})::{target:?}");
let mut resp = SignatureReceiver::default();
@ -276,8 +268,8 @@ impl<'a> PostTypeChecker<'a> {
self.check_element_of(
&container_ty,
false,
&container,
&mut check_signature(&mut resp, &target),
container,
&mut check_signature(&mut resp, target),
);
crate::log_debug_ct!("post check target iterated: {:?}", resp.bounds);
@ -287,7 +279,7 @@ impl<'a> PostTypeChecker<'a> {
container,
is_before,
} => {
let container_ty = self.check_context_or(&container, context_ty)?;
let container_ty = self.check_or(container, context_ty)?;
crate::log_debug_ct!("post check paren target: {container_ty:?}::{is_before:?}");
let mut resp = SignatureReceiver::default();
@ -299,7 +291,7 @@ impl<'a> PostTypeChecker<'a> {
self.check_element_of(
&container_ty,
false,
&container,
container,
&mut check_signature(&mut resp, &target),
);
@ -311,11 +303,14 @@ impl<'a> PostTypeChecker<'a> {
allow_package: true,
}),
)),
CheckTarget::LabelError(..) => Some(Ty::Builtin(BuiltinTy::Label)),
CheckTarget::Label(target) | CheckTarget::Normal(target) => {
let ty = self.check_context_or(&target, context_ty)?;
crate::log_debug_ct!("post check target normal: {ty:?}");
Some(ty)
CheckTarget::LabelError(target)
| CheckTarget::Label(target)
| CheckTarget::Normal(target) => {
let label_ty = matches!(node, CheckTarget::LabelError(_))
.then_some(Ty::Builtin(BuiltinTy::Label));
let ty = self.check_or(target, context_ty);
crate::log_debug_ct!("post check target normal: {ty:?} {label_ty:?}");
ty.or(label_ty)
}
}
}

View file

@ -0,0 +1,30 @@
/// path: references.yaml
tarry:
type: Book
title: Harry Potter and the Order of the Phoenix
author: Rowling, J. K.
volume: 5
page-total: 768
date: 2003-06-21
electronic:
type: Web
title: Ishkur's Guide to Electronic Music
serial-number: v2.5
author: Ishkur
url: http://www.techno.org/electronic-music-guide/
-----
/// path: base.typ
#set heading(numbering: "1.1")
= H <test>
#bibliography("references.yaml")
-----
/// contains: tarry, test
/// compile: base.typ
#cite(<t /* range -2..-1 */)

View file

@ -0,0 +1,35 @@
---
source: crates/tinymist-query/src/completion.rs
description: Completion on t (56..57)
expression: "JsonRepr::new_pure(results)"
input_file: crates/tinymist-query/src/fixtures/completion/complete_half_label_cite.typ
snapshot_kind: text
---
[
{
"isIncomplete": false,
"items": [
{
"kind": 18,
"label": "tarry",
"labelDetails": {
"description": "Harry Potter and the Order of the Phoenix"
},
"sortText": "003",
"textEdit": {
"newText": "tarry>",
"range": {
"end": {
"character": 7,
"line": 3
},
"start": {
"character": 7,
"line": 3
}
}
}
}
]
}
]

View file

@ -561,15 +561,16 @@ pub fn get_check_target_by_context<'a>(
context: LinkedNode<'a>,
node: LinkedNode<'a>,
) -> Option<CheckTarget<'a>> {
use DerefTarget::*;
let context_deref_target = get_deref_target(context.clone(), node.offset())?;
let node_deref_target = get_deref_target(node.clone(), node.offset())?;
match context_deref_target {
DerefTarget::Callee(callee)
Callee(callee)
if matches!(
node_deref_target,
DerefTarget::Normal(..) | DerefTarget::Label(..) | DerefTarget::Ref(..)
) && !matches!(node_deref_target, DerefTarget::Callee(..)) =>
Normal(..) | Label(..) | LabelError(..) | Ref(..)
) && !matches!(node_deref_target, Callee(..)) =>
{
let parent = callee.parent()?;
let args = match parent.cast::<ast::Expr>() {

View file

@ -144,11 +144,10 @@ impl Ty {
matches!(self, Ty::Dict(..))
}
pub(crate) fn or(ty: Option<Ty>, pos: Option<Ty>) -> Option<Ty> {
Some(match (ty, pos) {
(Some(ty), Some(pos)) => Ty::from_types([ty, pos].into_iter()),
(Some(ty), None) => ty,
(None, Some(pos)) => pos,
pub(crate) fn union(lhs: Option<Ty>, rhs: Option<Ty>) -> Option<Ty> {
Some(match (lhs, rhs) {
(Some(lhs), Some(rhs)) => Ty::from_types([lhs, rhs].into_iter()),
(Some(ty), None) | (None, Some(ty)) => ty,
(None, None) => return None,
})
}