feat: implement expression checker (#714)

* feat: implements expression checker

* dev: resolve information

* dev: delete def_use

* stage

* stage

* stage

* stage concurrent

* stage concurrent

* dev: better concurrency

* dev: final constant evaluation improvement

* dev: change reference site

* dev: handle comments

* dev: remove indirect import structure

* dev: adjust linked_def impl

* dev: finalize goto definition impl

* dev: replace all old import and def_use analyses with expr analysis

* dev: update expr_of snapshots

* dev: split def/expr, refactor definition

* dev: more consistent definition solver

* dev: rename definition crate

* dev: references work again

* dev: resolve root decl

* dev: resolve root decl

* dev: resolve global definitions

* dev: resolve tokens with world

* feat: render semantic tokens with expression information

* dev: loop detection

* dev: recover type checking

* dev: recover more type checking

* dev: refactor analysis context

* fix: process case of spread left

* dev: label inference

* dev: recover more signature checking

* dev: recover more ident reference checking

* dev: pass all tests

* Revert "dev: dirty changes"

This reverts commit 9ae2dacd0c96851e088feea76c61c184a1cf9722.

* test: update snapshot

* fix: bad cached signatures

* fix: slash problem
This commit is contained in:
Myriad-Dreamin 2024-10-25 23:52:11 +08:00 committed by GitHub
parent 136b162360
commit 81ebc8a635
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
173 changed files with 5529 additions and 4370 deletions

View file

@ -1,11 +1,11 @@
use std::ops::Range;
use log::debug;
use typst::syntax::Span;
use crate::{
analysis::{find_definition, SearchCtx},
analysis::{Definition, SearchCtx},
prelude::*,
syntax::{DerefTarget, IdentRef},
syntax::{DerefTarget, RefExpr},
ty::Interned,
};
/// The [`textDocument/references`] request is sent from the client to the
@ -32,7 +32,7 @@ impl StatefulRequest for ReferencesRequest {
let source = ctx.source_by_path(&self.path).ok()?;
let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?;
let locations = find_references(ctx, source.clone(), doc.as_ref(), deref_target)?;
let locations = find_references(ctx, &source, doc.as_ref(), deref_target)?;
debug!("references: {locations:?}");
Some(locations)
@ -40,12 +40,12 @@ impl StatefulRequest for ReferencesRequest {
}
pub(crate) fn find_references(
ctx: &mut AnalysisContext<'_>,
source: Source,
document: Option<&VersionedDocument>,
deref_target: DerefTarget<'_>,
ctx: &mut AnalysisContext,
source: &Source,
doc: Option<&VersionedDocument>,
target: DerefTarget<'_>,
) -> Option<Vec<LspLocation>> {
let finding_label = match deref_target {
let finding_label = match target {
DerefTarget::VarAccess(..) | DerefTarget::Callee(..) => false,
DerefTarget::Label(..) | DerefTarget::Ref(..) => true,
DerefTarget::ImportPath(..) | DerefTarget::IncludePath(..) | DerefTarget::Normal(..) => {
@ -53,48 +53,33 @@ pub(crate) fn find_references(
}
};
let def = find_definition(ctx, source, document, deref_target)?;
// todo: reference of builtin items?
let (def_fid, def_range) = def.def_at?;
let def_ident = IdentRef {
name: def.name.clone(),
range: def_range,
};
let def_source = ctx.source_by_id(def_fid).ok()?;
let root_def_use = ctx.def_use(def_source)?;
let root_def_id = root_def_use.get_def(def_fid, &def_ident).map(|e| e.0);
let def = ctx.definition(source, doc, target)?;
let worker = ReferencesWorker {
ctx: ctx.fork_for_search(),
references: vec![],
def_fid,
def_ident,
finding_label,
def,
};
if finding_label {
worker.label_root()
} else {
worker.ident_root(root_def_use, root_def_id?)
// todo: reference of builtin items?
worker.ident_root()
}
}
struct ReferencesWorker<'a, 'w> {
ctx: SearchCtx<'a, 'w>,
references: Vec<LspLocation>,
def_fid: TypstFileId,
def_ident: IdentRef,
finding_label: bool,
def: Definition,
}
impl<'a, 'w> ReferencesWorker<'a, 'w> {
fn label_root(mut self) -> Option<Vec<LspLocation>> {
let mut ids = vec![];
for dep in self.ctx.ctx.resources.dependencies() {
for dep in self.ctx.ctx.dependencies() {
if let Ok(ref_fid) = self.ctx.ctx.file_id_by_path(&dep) {
ids.push(ref_fid);
}
@ -107,22 +92,10 @@ impl<'a, 'w> ReferencesWorker<'a, 'w> {
Some(self.references)
}
fn ident_root(
mut self,
def_use: Arc<crate::analysis::DefUseInfo>,
def_id: DefId,
) -> Option<Vec<LspLocation>> {
let def_source = self.ctx.ctx.source_by_id(self.def_fid).ok()?;
let uri = self.ctx.ctx.uri_for_id(self.def_fid).ok()?;
self.push_idents(&def_source, &uri, def_use.get_refs(def_id));
if def_use.is_exported(def_id) {
// Find dependents
self.ctx.push_dependents(self.def_fid);
while let Some(ref_fid) = self.ctx.worklist.pop() {
self.file(ref_fid);
}
fn ident_root(mut self) -> Option<Vec<LspLocation>> {
self.file(self.def.decl.file_id()?);
while let Some(ref_fid) = self.ctx.worklist.pop() {
self.file(ref_fid);
}
Some(self.references)
@ -131,43 +104,36 @@ impl<'a, 'w> ReferencesWorker<'a, 'w> {
fn file(&mut self, ref_fid: TypstFileId) -> Option<()> {
log::debug!("references: file: {ref_fid:?}");
let ref_source = self.ctx.ctx.source_by_id(ref_fid).ok()?;
let def_use = self.ctx.ctx.def_use(ref_source.clone())?;
let expr_info = self.ctx.ctx.expr_stage(&ref_source);
let uri = self.ctx.ctx.uri_for_id(ref_fid).ok()?;
let mut redefines = vec![];
if let Some((id, _def)) = def_use.get_def(self.def_fid, &self.def_ident) {
self.push_idents(&ref_source, &uri, def_use.get_refs(id));
let t = expr_info.get_refs(self.def.decl.clone());
self.push_idents(&ref_source, &uri, t);
redefines.push(id);
if def_use.is_exported(id) {
self.ctx.push_dependents(ref_fid);
}
};
// All references are not resolved since static analyzers doesn't know anything
// about labels (which is working at runtime).
if self.finding_label {
let label_refs = def_use.label_refs.get(&self.def_ident.name);
self.push_ranges(&ref_source, &uri, label_refs.into_iter().flatten());
if expr_info.is_exported(&self.def.decl) {
self.ctx.push_dependents(ref_fid);
}
Some(())
}
fn push_idents<'b>(&mut self, s: &Source, u: &Url, idents: impl Iterator<Item = &'b IdentRef>) {
self.push_ranges(s, u, idents.map(|e| &e.range));
fn push_idents<'b>(
&mut self,
s: &Source,
u: &Url,
idents: impl Iterator<Item = (&'b Span, &'b Interned<RefExpr>)>,
) {
self.push_ranges(s, u, idents.map(|e| e.0));
}
fn push_ranges<'b>(&mut self, s: &Source, u: &Url, rs: impl Iterator<Item = &'b Range<usize>>) {
self.references.extend(rs.map(|rng| {
log::debug!("references: at file: {s:?}, {rng:?}");
let range = self.ctx.ctx.to_lsp_range(rng.clone(), s);
LspLocation {
fn push_ranges<'b>(&mut self, s: &Source, u: &Url, rs: impl Iterator<Item = &'b Span>) {
self.references.extend(rs.filter_map(|span| {
// todo: this is not necessary a name span
let range = self.ctx.ctx.to_lsp_range(s.range(*span)?, s);
Some(LspLocation {
uri: u.clone(),
range,
}
})
}));
}
}