mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-25 21:37:32 +00:00
feat: cross-module reference tracking
This commit is contained in:
parent
977fb2a219
commit
2c18389aa2
5 changed files with 104 additions and 14 deletions
|
|
@ -3,7 +3,10 @@
|
|||
mod collector;
|
||||
mod diagnostic;
|
||||
|
||||
use tinymist_analysis::syntax::{Decl, ExprInfo};
|
||||
use tinymist_analysis::{
|
||||
adt::interner::Interned,
|
||||
syntax::{Decl, ExprInfo},
|
||||
};
|
||||
use tinymist_project::LspWorld;
|
||||
use typst::ecow::EcoVec;
|
||||
|
||||
|
|
@ -32,7 +35,12 @@ impl Default for DeadCodeConfig {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_dead_code(world: &LspWorld, ei: &ExprInfo, config: &DeadCodeConfig) -> DiagnosticVec {
|
||||
pub fn check_dead_code(
|
||||
world: &LspWorld,
|
||||
ei: &ExprInfo,
|
||||
has_references: impl Fn(&Interned<Decl>) -> bool,
|
||||
config: &DeadCodeConfig,
|
||||
) -> DiagnosticVec {
|
||||
let mut diagnostics = EcoVec::new();
|
||||
|
||||
let definitions = collect_definitions(ei);
|
||||
|
|
@ -46,14 +54,7 @@ pub fn check_dead_code(world: &LspWorld, ei: &ExprInfo, config: &DeadCodeConfig)
|
|||
continue;
|
||||
}
|
||||
|
||||
let has_refs = {
|
||||
let target_decl = def_info.decl.clone();
|
||||
|
||||
ei.get_refs(target_decl.clone())
|
||||
.any(|(_, r)| r.as_ref().decl != target_decl)
|
||||
};
|
||||
|
||||
if !has_refs {
|
||||
if !has_references(&def_info.decl) {
|
||||
if let Some(diag) = generate_diagnostic(&def_info, world, ei) {
|
||||
diagnostics.push(diag);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,12 @@ pub fn lint_file(
|
|||
ei: &ExprInfo,
|
||||
ti: Arc<TypeInfo>,
|
||||
known_issues: KnownIssues,
|
||||
has_references: impl Fn(&Interned<Decl>) -> bool,
|
||||
) -> LintInfo {
|
||||
let mut diagnostics = Linter::new(world, ei.clone(), ti, known_issues).lint(ei.source.root());
|
||||
|
||||
let dead_code_diags = dead_code::check_dead_code(world, ei, &DeadCodeConfig::default());
|
||||
let dead_code_diags =
|
||||
dead_code::check_dead_code(world, ei, has_references, &DeadCodeConfig::default());
|
||||
diagnostics.extend(dead_code_diags);
|
||||
|
||||
LintInfo {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ use std::{collections::HashSet, ops::Deref};
|
|||
use comemo::{Track, Tracked};
|
||||
use lsp_types::Url;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use tinymist_analysis::adt::interner::Interned;
|
||||
use tinymist_analysis::docs::DocString;
|
||||
use tinymist_analysis::stats::AllocStats;
|
||||
use tinymist_analysis::syntax::classify_def_loosely;
|
||||
|
|
@ -39,7 +40,7 @@ use crate::analysis::{
|
|||
};
|
||||
use crate::docs::{DefDocs, TidyModuleDocs};
|
||||
use crate::syntax::{
|
||||
Decl, DefKind, ExprInfo, ExprRoute, LexicalScope, ModuleDependency, SyntaxClass,
|
||||
Decl, DefKind, Expr, ExprInfo, ExprRoute, LexicalScope, ModuleDependency, SyntaxClass,
|
||||
classify_syntax, construct_module_dependencies, is_mark, resolve_id_by_path,
|
||||
scan_workspace_files,
|
||||
};
|
||||
|
|
@ -828,10 +829,77 @@ impl SharedContext {
|
|||
let guard = self.query_stat(source.id(), "lint");
|
||||
self.slot.lint.compute(hash128(&(&ei, &ti, issues)), |_| {
|
||||
guard.miss();
|
||||
tinymist_lint::lint_file(self.world(), &ei, ti, issues.clone())
|
||||
|
||||
let cross_file_refs = self.compute_cross_file_references(source.id(), &ei);
|
||||
|
||||
let has_references = |decl: &Interned<Decl>| -> bool {
|
||||
if ei
|
||||
.get_refs(decl.clone())
|
||||
.any(|(_, r)| r.as_ref().decl != *decl)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
cross_file_refs.contains(decl)
|
||||
};
|
||||
|
||||
tinymist_lint::lint_file(self.world(), &ei, ti, issues.clone(), has_references)
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes which declarations from the current file are referenced by other files.
|
||||
fn compute_cross_file_references(
|
||||
self: &Arc<Self>,
|
||||
current_file: TypstFileId,
|
||||
current_ei: &ExprInfo,
|
||||
) -> FxHashSet<Interned<Decl>> {
|
||||
let mut referenced_decls = FxHashSet::default();
|
||||
let files: Vec<_> = self.world().depended_files().into_iter().collect();
|
||||
|
||||
let mut all_decls = Vec::new();
|
||||
|
||||
for (_, expr) in current_ei.exports.iter() {
|
||||
if let Expr::Decl(decl) = expr {
|
||||
all_decls.push(decl.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for (_, ref_expr) in current_ei.resolves.iter() {
|
||||
all_decls.push(ref_expr.decl.clone());
|
||||
}
|
||||
|
||||
for &file_id in &files {
|
||||
if file_id == current_file {
|
||||
continue;
|
||||
}
|
||||
|
||||
let src = match self.source_by_id(file_id) {
|
||||
Ok(src) => src,
|
||||
Err(e) => {
|
||||
log::debug!(
|
||||
"failed to load source file {:?} for cross-file reference check: {:?}",
|
||||
file_id,
|
||||
e
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let file_ei = self.expr_stage(&src);
|
||||
|
||||
for decl in &all_decls {
|
||||
if file_ei
|
||||
.get_refs(decl.clone())
|
||||
.any(|(_, r)| r.as_ref().decl != *decl)
|
||||
{
|
||||
referenced_decls.insert(decl.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
referenced_decls
|
||||
}
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
/// path: /main.typ
|
||||
|
||||
// Cross-module usage should count as a reference for dead code analysis.
|
||||
#import "ieee.typ": ieee
|
||||
|
||||
#show: ieee
|
||||
|
||||
-----
|
||||
/// path: /ieee.typ
|
||||
/// compile: main.typ
|
||||
|
||||
#let ieee(body) = body
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
assertion_line: 662
|
||||
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
|
||||
input_file: crates/tinymist-query/src/fixtures/dead_code/cross_module_usage.typ
|
||||
---
|
||||
{}
|
||||
Loading…
Add table
Add a link
Reference in a new issue