mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-04 10:18:16 +00:00
feat: pass world to linter (#1650)
This commit is contained in:
parent
9c98876dfb
commit
3d6c712565
6 changed files with 77 additions and 53 deletions
|
@ -14,14 +14,10 @@ rust-version.workspace = true
|
|||
[dependencies]
|
||||
typst-library.workspace = true
|
||||
typst.workspace = true
|
||||
tinymist-std.workspace = true
|
||||
tinymist-analysis.workspace = true
|
||||
tinymist-world = { workspace = true, features = ["system"] }
|
||||
parking_lot.workspace = true
|
||||
tinymist-project = { workspace = true, features = ["lsp"] }
|
||||
tinymist-std.workspace = true
|
||||
comemo.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
base64.workspace = true
|
||||
rayon.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -6,33 +6,52 @@ use tinymist_analysis::{
|
|||
syntax::ExprInfo,
|
||||
ty::{Ty, TyCtx, TypeInfo},
|
||||
};
|
||||
use tinymist_project::LspWorld;
|
||||
use typst::{
|
||||
diag::{eco_format, EcoString, SourceDiagnostic, Tracepoint},
|
||||
ecow::EcoVec,
|
||||
syntax::{
|
||||
ast::{self, AstNode},
|
||||
Span, Spanned, SyntaxNode,
|
||||
FileId, Span, Spanned, SyntaxNode,
|
||||
},
|
||||
};
|
||||
|
||||
/// A type alias for a vector of diagnostics.
|
||||
type DiagnosticVec = EcoVec<SourceDiagnostic>;
|
||||
|
||||
/// Performs linting check on file and returns a vector of diagnostics.
|
||||
pub fn lint_file(expr: &ExprInfo, ti: Arc<TypeInfo>) -> DiagnosticVec {
|
||||
Linter::new(ti).lint(expr.source.root())
|
||||
/// The lint information about a file.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LintInfo {
|
||||
/// The revision of expression information
|
||||
pub revision: usize,
|
||||
/// The belonging file id
|
||||
pub fid: FileId,
|
||||
/// The diagnostics
|
||||
pub diagnostics: DiagnosticVec,
|
||||
}
|
||||
|
||||
struct Linter {
|
||||
/// Performs linting check on file and returns a vector of diagnostics.
|
||||
pub fn lint_file(world: &LspWorld, expr: &ExprInfo, ti: Arc<TypeInfo>) -> LintInfo {
|
||||
let diagnostics = Linter::new(world, ti).lint(expr.source.root());
|
||||
LintInfo {
|
||||
revision: expr.revision,
|
||||
fid: expr.fid,
|
||||
diagnostics,
|
||||
}
|
||||
}
|
||||
|
||||
struct Linter<'w> {
|
||||
world: &'w LspWorld,
|
||||
ti: Arc<TypeInfo>,
|
||||
diag: DiagnosticVec,
|
||||
loop_info: Option<LoopInfo>,
|
||||
func_info: Option<FuncInfo>,
|
||||
}
|
||||
|
||||
impl Linter {
|
||||
fn new(ti: Arc<TypeInfo>) -> Self {
|
||||
impl<'w> Linter<'w> {
|
||||
fn new(world: &'w LspWorld, ti: Arc<TypeInfo>) -> Self {
|
||||
Self {
|
||||
world,
|
||||
ti,
|
||||
diag: EcoVec::new(),
|
||||
loop_info: None,
|
||||
|
@ -229,6 +248,8 @@ impl Linter {
|
|||
return None;
|
||||
}
|
||||
|
||||
let _ = self.world;
|
||||
|
||||
let diag =
|
||||
SourceDiagnostic::warning(expr.span(), "variable font is not supported by typst yet");
|
||||
let diag = diag.with_hint("consider using a static font instead. For more information, see https://github.com/typst/typst/issues/185");
|
||||
|
@ -238,7 +259,7 @@ impl Linter {
|
|||
}
|
||||
}
|
||||
|
||||
impl DataFlowVisitor for Linter {
|
||||
impl DataFlowVisitor for Linter<'_> {
|
||||
fn exprs<'a>(&mut self, exprs: impl DoubleEndedIterator<Item = ast::Expr<'a>>) -> Option<()> {
|
||||
for expr in exprs {
|
||||
self.expr(expr);
|
||||
|
@ -368,13 +389,13 @@ impl DataFlowVisitor for Linter {
|
|||
}
|
||||
}
|
||||
|
||||
struct LateFuncLinter<'a> {
|
||||
linter: &'a mut Linter,
|
||||
struct LateFuncLinter<'a, 'b> {
|
||||
linter: &'a mut Linter<'b>,
|
||||
func_info: FuncInfo,
|
||||
return_block_info: Option<ReturnBlockInfo>,
|
||||
}
|
||||
|
||||
impl LateFuncLinter<'_> {
|
||||
impl LateFuncLinter<'_, '_> {
|
||||
fn late_closure(&mut self, expr: ast::Closure<'_>) -> Option<()> {
|
||||
if !self.func_info.has_return {
|
||||
return Some(());
|
||||
|
@ -408,7 +429,7 @@ impl LateFuncLinter<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl DataFlowVisitor for LateFuncLinter<'_> {
|
||||
impl DataFlowVisitor for LateFuncLinter<'_, '_> {
|
||||
fn exprs<'a>(&mut self, exprs: impl DoubleEndedIterator<Item = ast::Expr<'a>>) -> Option<()> {
|
||||
for expr in exprs.rev() {
|
||||
self.expr(expr);
|
||||
|
|
|
@ -669,10 +669,8 @@ mod lint_tests {
|
|||
fn test() {
|
||||
snapshot_testing("lint", &|ctx, path| {
|
||||
let source = ctx.source_by_path(&path).unwrap();
|
||||
let expr = ctx.expr_stage(&source);
|
||||
let ti = ctx.type_check(&source);
|
||||
|
||||
let result = tinymist_lint::lint_file(&expr, ti);
|
||||
let result = ctx.lint(&source);
|
||||
let result = crate::diagnostics::DiagWorker::new(ctx).convert_all(result.iter());
|
||||
let result = result
|
||||
.into_iter()
|
||||
|
|
|
@ -11,13 +11,16 @@ use rustc_hash::FxHashMap;
|
|||
use tinymist_analysis::stats::AllocStats;
|
||||
use tinymist_analysis::ty::term_value;
|
||||
use tinymist_analysis::{analyze_expr_, analyze_import_};
|
||||
use tinymist_lint::LintInfo;
|
||||
use tinymist_project::{LspComputeGraph, LspWorld};
|
||||
use tinymist_std::hash::{hash128, FxDashMap};
|
||||
use tinymist_std::typst::TypstDocument;
|
||||
use tinymist_world::debug_loc::DataSource;
|
||||
use tinymist_world::vfs::{PathResolution, WorkspaceResolver};
|
||||
use tinymist_world::{EntryReader, DETACHED_ENTRY};
|
||||
use typst::diag::{eco_format, At, FileError, FileResult, SourceResult, StrResult};
|
||||
use typst::diag::{
|
||||
eco_format, At, FileError, FileResult, SourceDiagnostic, SourceResult, StrResult,
|
||||
};
|
||||
use typst::foundations::{Bytes, IntoValue, Module, StyleChain, Styles};
|
||||
use typst::introspection::Introspector;
|
||||
use typst::layout::Position;
|
||||
|
@ -463,6 +466,10 @@ impl LocalContext {
|
|||
cache.get_or_init(|| self.shared.type_check(source)).clone()
|
||||
}
|
||||
|
||||
pub(crate) fn lint(&mut self, source: &Source) -> EcoVec<SourceDiagnostic> {
|
||||
self.shared.lint(source).diagnostics
|
||||
}
|
||||
|
||||
/// Get the type check information of a source file.
|
||||
pub(crate) fn type_check_by_id(&mut self, id: TypstFileId) -> Arc<TypeInfo> {
|
||||
let cache = &self.caches.modules.entry(id).or_default().type_check;
|
||||
|
@ -759,17 +766,9 @@ impl SharedContext {
|
|||
let ei = self.expr_stage(source);
|
||||
let guard = self.query_stat(source.id(), "type_check");
|
||||
self.slot.type_check.compute(hash128(&ei), |prev| {
|
||||
let cache_hit = prev.and_then(|prev| {
|
||||
// todo: recursively check changed scheme type
|
||||
if prev.revision != ei.revision {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(prev)
|
||||
});
|
||||
|
||||
if let Some(prev) = cache_hit {
|
||||
return prev.clone();
|
||||
// todo: recursively check changed scheme type
|
||||
if let Some(cache_hint) = prev.filter(|prev| prev.revision == ei.revision) {
|
||||
return cache_hint;
|
||||
}
|
||||
|
||||
guard.miss();
|
||||
|
@ -777,6 +776,17 @@ impl SharedContext {
|
|||
})
|
||||
}
|
||||
|
||||
/// Get the lint result of a source file.
|
||||
pub(crate) fn lint(self: &Arc<Self>, source: &Source) -> LintInfo {
|
||||
let ei = self.expr_stage(source);
|
||||
let ti = self.type_check(source);
|
||||
let guard = self.query_stat(source.id(), "lint");
|
||||
self.slot.lint.compute(hash128(&(&ei, &ti)), |_prev| {
|
||||
guard.miss();
|
||||
tinymist_lint::lint_file(&self.world, &ei, ti)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn type_of_func(self: &Arc<Self>, func: Func) -> Signature {
|
||||
crate::log_debug_ct!("convert runtime func {func:?}");
|
||||
analyze_signature(self, SignatureTarget::Convert(func)).unwrap()
|
||||
|
@ -1180,6 +1190,7 @@ impl RevisionManagerLike for AnalysisRevCache {
|
|||
fn gc(&mut self, rev: usize) {
|
||||
self.manager.gc(rev);
|
||||
|
||||
// todo: the following code are time consuming.
|
||||
{
|
||||
let mut max_ei = FxHashMap::default();
|
||||
let es = self.default_slot.expr_stage.global.lock();
|
||||
|
@ -1199,6 +1210,16 @@ impl RevisionManagerLike for AnalysisRevCache {
|
|||
}
|
||||
ts.retain(|_, r| r.1.revision == *max_ti.get(&r.1.fid).unwrap_or(&0));
|
||||
}
|
||||
|
||||
{
|
||||
let mut max_li = FxHashMap::default();
|
||||
let ts = self.default_slot.lint.global.lock();
|
||||
for r in ts.iter() {
|
||||
let rev: &mut usize = max_li.entry(r.1.fid).or_default();
|
||||
*rev = (*rev).max(r.1.revision);
|
||||
}
|
||||
ts.retain(|_, r| r.1.revision == *max_li.get(&r.1.fid).unwrap_or(&0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1222,6 +1243,7 @@ impl AnalysisRevCache {
|
|||
revision: slot.revision,
|
||||
expr_stage: slot.data.expr_stage.crawl(revision.get()),
|
||||
type_check: slot.data.type_check.crawl(revision.get()),
|
||||
lint: slot.data.lint.crawl(revision.get()),
|
||||
})
|
||||
.unwrap_or_else(|| self.default_slot.clone())
|
||||
})
|
||||
|
@ -1254,6 +1276,7 @@ struct AnalysisRevSlot {
|
|||
revision: usize,
|
||||
expr_stage: IncrCacheMap<u128, ExprInfo>,
|
||||
type_check: IncrCacheMap<u128, Arc<TypeInfo>>,
|
||||
lint: IncrCacheMap<u128, LintInfo>,
|
||||
}
|
||||
|
||||
impl Drop for AnalysisRevSlot {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use tinymist_analysis::ty::TypeInfo;
|
||||
use tinymist_project::LspWorld;
|
||||
use tinymist_world::vfs::WorkspaceResolver;
|
||||
use typst::{diag::SourceDiagnostic, syntax::Span};
|
||||
use typst::syntax::Span;
|
||||
|
||||
use crate::{analysis::Analysis, prelude::*, syntax::ExprInfo, LspWorldExt};
|
||||
use crate::{analysis::Analysis, prelude::*, LspWorldExt};
|
||||
|
||||
use regex::RegexSet;
|
||||
|
||||
|
@ -57,13 +56,9 @@ impl<'w> DiagWorker<'w> {
|
|||
let Ok(source) = self.ctx.world.source(dep) else {
|
||||
continue;
|
||||
};
|
||||
let expr = self.ctx.expr_stage(&source);
|
||||
let ti = self.ctx.type_check(&source);
|
||||
let res = lint_file(&expr, ti);
|
||||
if !res.is_empty() {
|
||||
for diag in res {
|
||||
self.handle(&diag);
|
||||
}
|
||||
|
||||
for diag in self.ctx.lint(&source) {
|
||||
self.handle(&diag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,8 +241,3 @@ impl DiagnosticRefiner for OutOfRootHintRefiner {
|
|||
raw.with_hint("Cannot read file outside of project root.")
|
||||
}
|
||||
}
|
||||
|
||||
#[comemo::memoize]
|
||||
fn lint_file(source: &ExprInfo, ti: Arc<TypeInfo>) -> EcoVec<SourceDiagnostic> {
|
||||
tinymist_lint::lint_file(source, ti)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue