mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 09:52:27 +00:00
feat: feed more information to linter (#1642)
* feat: move expr struct to common crate * feat: feed more information to linter
This commit is contained in:
parent
fcb060280d
commit
6cf7739fb6
10 changed files with 205 additions and 202 deletions
|
@ -669,8 +669,9 @@ 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 result = tinymist_lint::lint_source(&source);
|
||||
let result = tinymist_lint::lint_source(&expr);
|
||||
let result = crate::diagnostics::DiagWorker::new(ctx).convert_all(result.iter());
|
||||
let result = result
|
||||
.into_iter()
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
use tinymist_analysis::docs::DocString;
|
||||
use tinymist_std::TakeAs;
|
||||
|
||||
use super::*;
|
||||
use crate::syntax::DocString;
|
||||
|
||||
impl TypeChecker<'_> {
|
||||
pub fn check_docstring(&mut self, base_id: &Interned<Decl>) -> Option<Arc<DocString>> {
|
||||
let docstring = self.ei.docstrings.get(base_id)?.clone();
|
||||
Some(Arc::new(
|
||||
docstring.take().rename_based_on(base_id.clone(), self),
|
||||
self.rename_based_on(docstring.take(), base_id.clone()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl DocString {
|
||||
fn rename_based_on(self, documenting_id: Interned<Decl>, base: &mut TypeChecker) -> DocString {
|
||||
fn rename_based_on(&mut self, docs: DocString, documenting_id: Interned<Decl>) -> DocString {
|
||||
let DocString {
|
||||
docs,
|
||||
var_bounds,
|
||||
vars,
|
||||
mut res_ty,
|
||||
} = self;
|
||||
} = docs;
|
||||
let mut renamer = IdRenamer {
|
||||
base,
|
||||
base: self,
|
||||
var_bounds: &var_bounds,
|
||||
base_id: documenting_id,
|
||||
};
|
||||
|
|
|
@ -4,8 +4,8 @@ use typst::foundations::{Element, Type};
|
|||
|
||||
use super::*;
|
||||
use crate::analysis::ParamAttrs;
|
||||
use crate::docs::{SignatureDocsT, TypelessParamDocs, UntypedDefDocs};
|
||||
use crate::syntax::{def::*, DocString, VarDoc};
|
||||
use crate::docs::{DocString, SignatureDocsT, TypelessParamDocs, UntypedDefDocs, VarDoc};
|
||||
use crate::syntax::def::*;
|
||||
use crate::ty::*;
|
||||
|
||||
static EMPTY_DOCSTRING: LazyLock<DocString> = LazyLock::new(DocString::default);
|
||||
|
|
|
@ -4,7 +4,7 @@ use tinymist_project::LspWorld;
|
|||
use tinymist_world::vfs::WorkspaceResolver;
|
||||
use typst::{diag::SourceDiagnostic, syntax::Span};
|
||||
|
||||
use crate::{analysis::Analysis, prelude::*, LspWorldExt};
|
||||
use crate::{analysis::Analysis, prelude::*, syntax::ExprInfo, LspWorldExt};
|
||||
|
||||
use regex::RegexSet;
|
||||
|
||||
|
@ -56,7 +56,8 @@ impl<'w> DiagWorker<'w> {
|
|||
let Ok(source) = self.ctx.world.source(dep) else {
|
||||
continue;
|
||||
};
|
||||
let res = lint_source(&source);
|
||||
let expr = self.ctx.expr_stage(&source);
|
||||
let res = lint_file(&expr);
|
||||
if !res.is_empty() {
|
||||
for diag in res {
|
||||
self.handle(&diag);
|
||||
|
@ -245,6 +246,6 @@ impl DiagnosticRefiner for OutOfRootHintRefiner {
|
|||
}
|
||||
|
||||
#[comemo::memoize]
|
||||
fn lint_source(source: &Source) -> EcoVec<SourceDiagnostic> {
|
||||
fn lint_file(source: &ExprInfo) -> EcoVec<SourceDiagnostic> {
|
||||
tinymist_lint::lint_source(source)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
ops::Deref,
|
||||
sync::{LazyLock, OnceLock},
|
||||
};
|
||||
use std::{collections::BTreeMap, ops::Deref, sync::LazyLock};
|
||||
|
||||
use ecow::eco_format;
|
||||
use typst::foundations::{IntoValue, Module, Str, Type};
|
||||
|
@ -10,7 +6,7 @@ use typst::foundations::{IntoValue, Module, Str, Type};
|
|||
use crate::{adt::interner::Interned, StrRef};
|
||||
use crate::{adt::snapshot_map::SnapshotMap, analysis::SharedContext};
|
||||
use crate::{
|
||||
docs::{convert_docs, identify_pat_docs, identify_tidy_module_docs, UntypedDefDocs, VarDocsT},
|
||||
docs::{convert_docs, identify_pat_docs, identify_tidy_module_docs, DocString, VarDoc},
|
||||
prelude::*,
|
||||
syntax::{Decl, DefKind},
|
||||
ty::{BuiltinTy, DynTypeBounds, InsTy, PackageId, SigTy, Ty, TypeVar, TypeVarBounds},
|
||||
|
@ -18,59 +14,6 @@ use crate::{
|
|||
|
||||
use super::DeclExpr;
|
||||
|
||||
/// The documentation string of an item
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DocString {
|
||||
/// The documentation of the item
|
||||
pub docs: Option<EcoString>,
|
||||
/// The typing on definitions
|
||||
pub var_bounds: HashMap<DeclExpr, TypeVarBounds>,
|
||||
/// The variable doc associated with the item
|
||||
pub vars: BTreeMap<StrRef, VarDoc>,
|
||||
/// The type of the resultant type
|
||||
pub res_ty: Option<Ty>,
|
||||
}
|
||||
|
||||
impl DocString {
|
||||
/// Gets the docstring as a variable doc
|
||||
pub fn as_var(&self) -> VarDoc {
|
||||
VarDoc {
|
||||
docs: self.docs.clone().unwrap_or_default(),
|
||||
ty: self.res_ty.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the documentation of a variable associated with the item
|
||||
pub fn get_var(&self, name: &StrRef) -> Option<&VarDoc> {
|
||||
self.vars.get(name)
|
||||
}
|
||||
|
||||
/// Get the type of a variable associated with the item
|
||||
pub fn var_ty(&self, name: &StrRef) -> Option<&Ty> {
|
||||
self.get_var(name).and_then(|v| v.ty.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// The documentation string of a variable associated with some item.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct VarDoc {
|
||||
/// The documentation of the variable
|
||||
pub docs: EcoString,
|
||||
/// The type of the variable
|
||||
pub ty: Option<Ty>,
|
||||
}
|
||||
|
||||
impl VarDoc {
|
||||
/// Convert the variable doc to an untyped version
|
||||
pub fn to_untyped(&self) -> Arc<UntypedDefDocs> {
|
||||
Arc::new(UntypedDefDocs::Variable(VarDocsT {
|
||||
docs: self.docs.clone(),
|
||||
return_ty: (),
|
||||
def_docs: OnceLock::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compute_docstring(
|
||||
ctx: &Arc<SharedContext>,
|
||||
fid: TypstFileId,
|
||||
|
|
|
@ -18,12 +18,13 @@ use typst::{
|
|||
|
||||
use crate::{
|
||||
analysis::{QueryStatGuard, SharedContext},
|
||||
docs::DocString,
|
||||
prelude::*,
|
||||
syntax::{find_module_level_docs, resolve_id_by_path, DefKind},
|
||||
ty::{BuiltinTy, InsTy, Ty},
|
||||
};
|
||||
|
||||
use super::{compute_docstring, def::*, DocCommentMatcher, DocString, InterpretMode};
|
||||
use super::{compute_docstring, def::*, DocCommentMatcher, InterpretMode};
|
||||
|
||||
pub type ExprRoute = FxHashMap<TypstFileId, Option<Arc<LazyHash<LexicalScope>>>>;
|
||||
|
||||
|
@ -76,18 +77,8 @@ pub(crate) fn expr_of(
|
|||
let docstrings_base = Arc::new(Mutex::new(FxHashMap::default()));
|
||||
let docstrings = docstrings_base.clone();
|
||||
|
||||
let exprs_base: Arc<
|
||||
parking_lot::lock_api::Mutex<
|
||||
parking_lot::RawMutex,
|
||||
HashMap<Span, Expr, rustc_hash::FxBuildHasher>,
|
||||
>,
|
||||
> = Arc::new(Mutex::new(FxHashMap::default()));
|
||||
let exprs: Arc<
|
||||
parking_lot::lock_api::Mutex<
|
||||
parking_lot::RawMutex,
|
||||
HashMap<Span, Expr, rustc_hash::FxBuildHasher>,
|
||||
>,
|
||||
> = exprs_base.clone();
|
||||
let exprs_base = Arc::new(Mutex::new(FxHashMap::default()));
|
||||
let exprs = exprs_base.clone();
|
||||
|
||||
let imports_base = Arc::new(Mutex::new(FxHashMap::default()));
|
||||
let imports = imports_base.clone();
|
||||
|
@ -145,109 +136,7 @@ pub(crate) fn expr_of(
|
|||
crate::log_debug_ct!("expr_of end {:?}", source.id());
|
||||
|
||||
route.remove(&info.fid);
|
||||
ExprInfo(Arc::new(LazyHash::new(info)))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct ExprInfo(Arc<LazyHash<ExprInfoRepr>>);
|
||||
|
||||
impl Deref for ExprInfo {
|
||||
type Target = Arc<LazyHash<ExprInfoRepr>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExprInfoRepr {
|
||||
pub fid: TypstFileId,
|
||||
pub revision: usize,
|
||||
pub source: Source,
|
||||
pub resolves: FxHashMap<Span, Interned<RefExpr>>,
|
||||
pub module_docstring: Arc<DocString>,
|
||||
pub docstrings: FxHashMap<DeclExpr, Arc<DocString>>,
|
||||
pub exprs: FxHashMap<Span, Expr>,
|
||||
pub imports: FxHashMap<TypstFileId, Arc<LazyHash<LexicalScope>>>,
|
||||
pub exports: Arc<LazyHash<LexicalScope>>,
|
||||
pub root: Expr,
|
||||
}
|
||||
|
||||
impl std::hash::Hash for ExprInfoRepr {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.revision.hash(state);
|
||||
self.source.hash(state);
|
||||
self.exports.hash(state);
|
||||
self.root.hash(state);
|
||||
let mut resolves = self.resolves.iter().collect::<Vec<_>>();
|
||||
resolves.sort_by_key(|(fid, _)| fid.into_raw());
|
||||
resolves.hash(state);
|
||||
let mut imports = self.imports.iter().collect::<Vec<_>>();
|
||||
imports.sort_by_key(|(fid, _)| *fid);
|
||||
imports.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ExprInfoRepr {
|
||||
pub fn get_def(&self, decl: &Interned<Decl>) -> Option<Expr> {
|
||||
if decl.is_def() {
|
||||
return Some(Expr::Decl(decl.clone()));
|
||||
}
|
||||
let resolved = self.resolves.get(&decl.span())?;
|
||||
Some(Expr::Ref(resolved.clone()))
|
||||
}
|
||||
|
||||
pub fn get_refs(
|
||||
&self,
|
||||
decl: Interned<Decl>,
|
||||
) -> impl Iterator<Item = (&Span, &Interned<RefExpr>)> {
|
||||
let of = Some(Expr::Decl(decl.clone()));
|
||||
self.resolves
|
||||
.iter()
|
||||
.filter(move |(_, r)| match (decl.as_ref(), r.decl.as_ref()) {
|
||||
(Decl::Label(..), Decl::Label(..)) => r.decl == decl,
|
||||
(Decl::Label(..), Decl::ContentRef(..)) => r.decl.name() == decl.name(),
|
||||
(Decl::Label(..), _) => false,
|
||||
_ => r.decl == decl || r.root == of,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_exported(&self, decl: &Interned<Decl>) -> bool {
|
||||
let of = Expr::Decl(decl.clone());
|
||||
self.exports
|
||||
.get(decl.name())
|
||||
.is_some_and(|export| match export {
|
||||
Expr::Ref(ref_expr) => ref_expr.root == Some(of),
|
||||
exprt => *exprt == of,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn show(&self) {
|
||||
use std::io::Write;
|
||||
let vpath = self
|
||||
.fid
|
||||
.vpath()
|
||||
.resolve(Path::new("target/exprs/"))
|
||||
.unwrap();
|
||||
let root = vpath.with_extension("root.expr");
|
||||
std::fs::create_dir_all(root.parent().unwrap()).unwrap();
|
||||
std::fs::write(root, format!("{}", self.root)).unwrap();
|
||||
let scopes = vpath.with_extension("scopes.expr");
|
||||
std::fs::create_dir_all(scopes.parent().unwrap()).unwrap();
|
||||
{
|
||||
let mut scopes = std::fs::File::create(scopes).unwrap();
|
||||
for (span, expr) in self.exprs.iter() {
|
||||
writeln!(scopes, "{span:?} -> {expr}").unwrap();
|
||||
}
|
||||
}
|
||||
let imports = vpath.with_extension("imports.expr");
|
||||
std::fs::create_dir_all(imports.parent().unwrap()).unwrap();
|
||||
std::fs::write(imports, format!("{:#?}", self.imports)).unwrap();
|
||||
let exports = vpath.with_extension("exports.expr");
|
||||
std::fs::create_dir_all(exports.parent().unwrap()).unwrap();
|
||||
std::fs::write(exports, format!("{:#?}", self.exports)).unwrap();
|
||||
}
|
||||
ExprInfo::new(info)
|
||||
}
|
||||
|
||||
type ConcolicExpr = (Option<Expr>, Option<Ty>);
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
pub(crate) mod lexical_hierarchy;
|
||||
pub use lexical_hierarchy::*;
|
||||
pub(crate) mod module;
|
||||
pub use module::*;
|
||||
pub(crate) mod expr;
|
||||
pub use expr::*;
|
||||
pub(crate) mod docs;
|
||||
pub use docs::*;
|
||||
pub(crate) mod expr;
|
||||
pub(crate) mod index;
|
||||
pub(crate) mod lexical_hierarchy;
|
||||
pub(crate) mod module;
|
||||
|
||||
pub(crate) use docs::*;
|
||||
pub use expr::*;
|
||||
pub use index::*;
|
||||
pub use lexical_hierarchy::*;
|
||||
pub use module::*;
|
||||
pub use tinymist_analysis::syntax::*;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue