rename dead code to unused

This commit is contained in:
Hong Jiarong 2025-12-17 19:47:24 +08:00
parent 9fdf82ae08
commit 5e17df077b
11 changed files with 96 additions and 101 deletions

View file

@ -80,7 +80,7 @@ pub fn query_main(mut cmds: QueryCommands) -> Result<()> {
_ => tinymist_query::ColorTheme::Light,
},
lint: config.lint.when().clone(),
dead_code: config.lint.dead_code_config(),
unused: config.lint.unused_config(),
periscope: None,
local_packages: Arc::default(),
tokens_caches: Arc::default(),

View file

@ -1,8 +1,8 @@
//! A linter for Typst.
mod dead_code;
mod unused;
pub use dead_code::DeadCodeConfig;
pub use unused::UnusedConfig;
/// Hint added to diagnostics for documented exported functions, to mark them as
/// likely public API.
@ -49,30 +49,30 @@ pub fn lint_file(
known_issues: KnownIssues,
) -> LintInfo {
let cross_file_refs: FxHashSet<Interned<Decl>> = FxHashSet::default();
let dead_code_config = DeadCodeConfig::default();
lint_file_with_dead_code_config(
let unused_config = UnusedConfig::default();
lint_file_with_unused_config(
world,
ei,
ti,
known_issues,
&cross_file_refs,
&dead_code_config,
&unused_config,
)
}
/// Performs linting check on file with a custom dead-code configuration.
pub fn lint_file_with_dead_code_config(
/// Performs linting check on file with a custom unused configuration.
pub fn lint_file_with_unused_config(
world: &LspWorld,
ei: &ExprInfo,
ti: Arc<TypeInfo>,
known_issues: KnownIssues,
cross_file_refs: &FxHashSet<Interned<Decl>>,
dead_code_config: &DeadCodeConfig,
unused_config: &UnusedConfig,
) -> 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, cross_file_refs, dead_code_config);
diagnostics.extend(dead_code_diags);
let unused_diags = unused::check_unused(world, ei, cross_file_refs, unused_config);
diagnostics.extend(unused_diags);
LintInfo {
revision: ei.revision,

View file

@ -1,4 +1,4 @@
//! Definition collector for dead code analysis.
//! Definition collector for unused declaration analysis.
use rustc_hash::FxHashSet;
use tinymist_analysis::{
@ -64,10 +64,10 @@ struct DefinitionCollector<'a> {
impl<'a> DefinitionCollector<'a> {
fn collect_exports(&mut self) {
for (_name, expr) in self.ei.exports.iter() {
if let Some(decl) = Self::extract_decl(expr) {
if decl.is_def() {
self.add_definition(decl, DefScope::Exported);
}
if let Some(decl) = Self::extract_decl(expr)
&& decl.is_def()
{
self.add_definition(decl, DefScope::Exported);
}
}
}

View file

@ -1,4 +1,4 @@
//! Diagnostic generator for dead code warnings.
//! Diagnostic generator for unused declaration warnings.
//!
//! This module creates user-friendly diagnostic messages for unused
//! definitions, with appropriate hints and severity levels.
@ -81,15 +81,14 @@ pub fn generate_diagnostic(
}
// Add kind-specific hints
if let DefKind::Function = def_info.kind {
if matches!(def_info.scope, DefScope::Exported)
&& ei.docstrings.contains_key(&def_info.decl)
{
diag = diag.with_hint(DOCUMENTED_EXPORTED_FUNCTION_HINT);
diag = diag.with_hint(
if let DefKind::Function = def_info.kind
&& matches!(def_info.scope, DefScope::Exported)
&& ei.docstrings.contains_key(&def_info.decl)
{
diag = diag.with_hint(DOCUMENTED_EXPORTED_FUNCTION_HINT);
diag = diag.with_hint(
"if this is intended public API, you can ignore this diagnostic; otherwise consider removing it",
);
}
}
if is_module_like {

View file

@ -1,4 +1,4 @@
//! Dead code detection for Typst.
//! Unused declaration detection for Typst.
mod collector;
mod diagnostic;
@ -26,9 +26,9 @@ struct ImportUsageInfo {
module_used_decls: FxHashSet<Interned<Decl>>,
}
/// Configuration for dead code detection.
/// Configuration for unused declaration detection.
#[derive(Debug, Clone, Hash)]
pub struct DeadCodeConfig {
pub struct UnusedConfig {
/// Whether to check exported but unused symbols.
pub check_exported: bool,
/// Whether to check unused function parameters.
@ -37,7 +37,7 @@ pub struct DeadCodeConfig {
pub exceptions: Vec<String>,
}
impl Default for DeadCodeConfig {
impl Default for UnusedConfig {
fn default() -> Self {
Self {
check_exported: false,
@ -47,11 +47,11 @@ impl Default for DeadCodeConfig {
}
}
pub fn check_dead_code(
pub fn check_unused(
world: &LspWorld,
ei: &ExprInfo,
cross_file_refs: &FxHashSet<Interned<Decl>>,
config: &DeadCodeConfig,
config: &UnusedConfig,
) -> DiagnosticVec {
let mut diagnostics = EcoVec::new();
@ -129,10 +129,8 @@ pub fn check_dead_code(
_ => !has_references(&def_info.decl),
};
if is_unused {
if let Some(diag) = generate_diagnostic(&def_info, world, ei) {
diagnostics.push(diag);
}
if is_unused && let Some(diag) = generate_diagnostic(&def_info, world, ei) {
diagnostics.push(diag);
}
}
@ -157,26 +155,24 @@ fn compute_import_usage(definitions: &[DefInfo], ei: &ExprInfo) -> ImportUsageIn
if matches!(
def.decl.as_ref(),
Decl::ModuleImport(_) | Decl::ModuleAlias(_)
) {
if let Some(r) = ei.resolves.get(&def.span) {
let fid = r
.root
.as_ref()
.and_then(|expr| expr.file_id())
.or_else(|| r.step.as_ref().and_then(|expr| expr.file_id()));
if let Some(fid) = fid {
module_targets.insert(def.decl.clone(), fid);
}
) && let Some(r) = ei.resolves.get(&def.span)
{
let fid = r
.root
.as_ref()
.and_then(|expr| expr.file_id())
.or_else(|| r.step.as_ref().and_then(|expr| expr.file_id()));
if let Some(fid) = fid {
module_targets.insert(def.decl.clone(), fid);
}
}
if matches!(def.decl.as_ref(), Decl::ImportAlias(_)) {
if let Some(alias_ref) = ei.resolves.get(&def.span) {
if let Some(Expr::Decl(step_decl)) = alias_ref.step.as_ref() {
alias_links.insert(def.decl.clone(), step_decl.clone());
shadowed.insert(step_decl.clone());
}
}
if matches!(def.decl.as_ref(), Decl::ImportAlias(_))
&& let Some(alias_ref) = ei.resolves.get(&def.span)
&& let Some(Expr::Decl(step_decl)) = alias_ref.step.as_ref()
{
alias_links.insert(def.decl.clone(), step_decl.clone());
shadowed.insert(step_decl.clone());
}
}
@ -195,10 +191,10 @@ fn compute_import_usage(definitions: &[DefInfo], ei: &ExprInfo) -> ImportUsageIn
.collect();
while let Some(alias) = worklist.pop() {
if let Some(target) = alias_links.get(&alias) {
if used.insert(target.clone()) {
worklist.push(target.clone());
}
if let Some(target) = alias_links.get(&alias)
&& used.insert(target.clone())
{
worklist.push(target.clone());
}
}
@ -309,7 +305,7 @@ fn collect_used_decls(reference: &RefExpr, used: &mut FxHashSet<Interned<Decl>>)
}
}
fn should_skip_definition(def_info: &DefInfo, config: &DeadCodeConfig) -> bool {
fn should_skip_definition(def_info: &DefInfo, config: &UnusedConfig) -> bool {
if matches!(def_info.scope, DefScope::Exported) && !config.check_exported {
return true;
}

View file

@ -629,7 +629,7 @@ mod call_info_tests {
mod lint_tests {
use std::collections::BTreeMap;
use tinymist_lint::{DeadCodeConfig, KnownIssues};
use tinymist_lint::{KnownIssues, UnusedConfig};
use crate::tests::*;
@ -671,11 +671,11 @@ mod lint_tests {
let ti = ctx.type_check(&source);
let cross_file_refs = rustc_hash::FxHashSet::default();
let dead_code_config = DeadCodeConfig {
let dead_code_config = UnusedConfig {
check_exported: true,
..DeadCodeConfig::default()
..UnusedConfig::default()
};
let result = tinymist_lint::lint_file_with_dead_code_config(
let result = tinymist_lint::lint_file_with_unused_config(
ctx.world(),
&ei,
ti,

View file

@ -14,7 +14,7 @@ use tinymist_analysis::stats::AllocStats;
use tinymist_analysis::syntax::classify_def_loosely;
use tinymist_analysis::ty::{BuiltinTy, InsTy, term_value};
use tinymist_analysis::{analyze_expr_, analyze_import_};
use tinymist_lint::{DeadCodeConfig, KnownIssues, LintInfo};
use tinymist_lint::{KnownIssues, LintInfo, UnusedConfig};
use tinymist_project::{LspComputeGraph, LspWorld, TaskWhen};
use tinymist_std::hash::{FxDashMap, hash128};
use tinymist_std::typst::TypstDocument;
@ -83,8 +83,8 @@ pub struct Analysis {
pub color_theme: ColorTheme,
/// When to trigger the lint.
pub lint: TaskWhen,
/// Configuration for dead code detection.
pub dead_code: DeadCodeConfig,
/// Configuration for unused declaration detection.
pub unused: UnusedConfig,
/// The periscope provider.
pub periscope: Option<Arc<dyn PeriscopeProvider + Send + Sync>>,
/// The global worker resources for analysis.
@ -839,23 +839,22 @@ impl SharedContext {
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, issues, &self.analysis.dead_code)),
|_| {
self.slot
.lint
.compute(hash128(&(&ei, &ti, issues, &self.analysis.unused)), |_| {
guard.miss();
let cross_file_refs = self.compute_cross_file_references(source.id(), &ei);
tinymist_lint::lint_file_with_dead_code_config(
tinymist_lint::lint_file_with_unused_config(
self.world(),
&ei,
ti,
issues.clone(),
&cross_file_refs,
&self.analysis.dead_code,
&self.analysis.unused,
)
},
)
})
}
/// Computes which declarations from the current file are referenced by other files.

View file

@ -980,13 +980,14 @@ pub struct LintFeat {
pub enabled: Option<bool>,
/// When to trigger the lint checks.
pub when: Option<TaskWhen>,
/// Dead code detection options.
/// Unused declaration detection options.
#[serde(
default,
rename = "deadCode",
rename = "unused",
alias = "deadCode",
deserialize_with = "deserialize_null_default"
)]
pub dead_code: DeadCodeFeat,
pub unused: UnusedFeat,
}
impl LintFeat {
@ -999,26 +1000,26 @@ impl LintFeat {
self.when.as_ref().unwrap_or(&TaskWhen::OnSave)
}
/// Gets the dead-code lint configuration derived from `lint.deadCode`.
pub fn dead_code_config(&self) -> tinymist_lint::DeadCodeConfig {
let mut config = tinymist_lint::DeadCodeConfig::default();
if let Some(check_exported) = self.dead_code.check_exported {
/// Gets the unused lint configuration derived from `lint.unused`.
pub fn unused_config(&self) -> tinymist_lint::UnusedConfig {
let mut config = tinymist_lint::UnusedConfig::default();
if let Some(check_exported) = self.unused.check_exported {
config.check_exported = check_exported;
}
if let Some(check_params) = self.dead_code.check_params {
if let Some(check_params) = self.unused.check_params {
config.check_params = check_params;
}
if let Some(exceptions) = &self.dead_code.exceptions {
if let Some(exceptions) = &self.unused.exceptions {
config.exceptions = exceptions.clone();
}
config
}
}
/// Dead code detection options under `lint.deadCode`.
/// Unused declaration detection options under `lint.unused`.
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeadCodeFeat {
pub struct UnusedFeat {
/// Whether to check module-level (exported) symbols.
pub check_exported: Option<bool>,
/// Whether to check unused function parameters.
@ -1311,10 +1312,10 @@ mod tests {
test_good_config("lint");
test_good_config("lint.enabled");
test_good_config("lint.when");
test_good_config("lint.deadCode");
test_good_config("lint.deadCode.checkExported");
test_good_config("lint.deadCode.checkParams");
test_good_config("lint.deadCode.exceptions");
test_good_config("lint.unused");
test_good_config("lint.unused.checkExported");
test_good_config("lint.unused.checkParams");
test_good_config("lint.unused.exceptions");
test_good_config("preview");
test_good_config("preview.browsing");

View file

@ -177,7 +177,7 @@ impl ServerState {
_ => tinymist_query::ColorTheme::Light,
},
lint: config.lint.when().clone(),
dead_code: config.lint.dead_code_config(),
unused: config.lint.unused_config(),
periscope: periscope_args.map(|args| {
let r = TypstPeriscopeProvider(PeriscopeRenderer::new(args));
Arc::new(r) as Arc<dyn PeriscopeProvider + Send + Sync>

View file

@ -454,21 +454,21 @@
"%extension.tinymist.config.tinymist.lint.when.string.enum.onType%"
]
},
"tinymist.lint.deadCode.checkExported": {
"title": "%extension.tinymist.config.tinymist.lint.deadCode.checkExported.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.lint.deadCode.checkExported.desc%",
"tinymist.lint.unused.checkExported": {
"title": "%extension.tinymist.config.tinymist.lint.unused.checkExported.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.lint.unused.checkExported.desc%",
"type": "boolean",
"default": false
},
"tinymist.lint.deadCode.checkParams": {
"title": "%extension.tinymist.config.tinymist.lint.deadCode.checkParams.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.lint.deadCode.checkParams.desc%",
"tinymist.lint.unused.checkParams": {
"title": "%extension.tinymist.config.tinymist.lint.unused.checkParams.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.lint.unused.checkParams.desc%",
"type": "boolean",
"default": true
},
"tinymist.lint.deadCode.exceptions": {
"title": "%extension.tinymist.config.tinymist.lint.deadCode.exceptions.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.lint.deadCode.exceptions.desc%",
"tinymist.lint.unused.exceptions": {
"title": "%extension.tinymist.config.tinymist.lint.unused.exceptions.title%",
"markdownDescription": "%extension.tinymist.config.tinymist.lint.unused.exceptions.desc%",
"type": "array",
"items": {
"type": "string"

View file

@ -1603,26 +1603,26 @@ zh = "保存文件时执行代码检查"
en = "Perform lint checks on type"
zh = "标记文件时执行代码检查"
[extension.tinymist.config.tinymist.lint.deadCode.checkExported.title]
[extension.tinymist.config.tinymist.lint.unused.checkExported.title]
en = "Check exported symbols"
zh = "检查导出符号"
[extension.tinymist.config.tinymist.lint.deadCode.checkExported.desc]
[extension.tinymist.config.tinymist.lint.unused.checkExported.desc]
en = "Whether to check module-level (exported) symbols for being unused."
zh = "是否检查模块级(导出)符号是否未使用。"
[extension.tinymist.config.tinymist.lint.deadCode.checkParams.title]
[extension.tinymist.config.tinymist.lint.unused.checkParams.title]
en = "Check unused parameters"
zh = "检查未使用参数"
[extension.tinymist.config.tinymist.lint.deadCode.checkParams.desc]
[extension.tinymist.config.tinymist.lint.unused.checkParams.desc]
en = "Whether to check unused function parameters."
zh = "是否检查未使用的函数参数。"
[extension.tinymist.config.tinymist.lint.deadCode.exceptions.title]
[extension.tinymist.config.tinymist.lint.unused.exceptions.title]
en = "Dead code exceptions"
zh = "死代码例外规则"
[extension.tinymist.config.tinymist.lint.deadCode.exceptions.desc]
[extension.tinymist.config.tinymist.lint.unused.exceptions.desc]
en = "Glob-like patterns for exceptions (e.g., \"test_*\", \"_*\")."
zh = "用于例外的类似 glob 的模式(例如 \"test_*\"、\"_*\")。"