From f40199bea21507c59798dc9ec8653ccf2fbf143a Mon Sep 17 00:00:00 2001 From: oxalica Date: Sat, 4 Mar 2023 01:53:52 +0800 Subject: [PATCH] Warn unused pat parameters for modules and packages --- crates/ide/src/def/liveness.rs | 42 +++++++++++++++++++++++++++++++--- docs/features.md | 3 ++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/crates/ide/src/def/liveness.rs b/crates/ide/src/def/liveness.rs index 6b4e935..6e53750 100644 --- a/crates/ide/src/def/liveness.rs +++ b/crates/ide/src/def/liveness.rs @@ -16,8 +16,9 @@ //! - Unused `let` bindings. //! - Unused `with` expressions. //! - Unnecessary `rec` attrsets. +//! - Unused parameters of a package. use super::{BindingValue, DefDatabase, Expr, ExprId, NameId, ResolveResult}; -use crate::{Diagnostic, DiagnosticKind, FileId}; +use crate::{Diagnostic, DiagnosticKind, FileId, ModuleKind}; use la_arena::ArenaMap; use std::collections::HashMap; use std::sync::Arc; @@ -75,6 +76,13 @@ pub(crate) fn liveness_check_query( let module = db.module(file_id); let name_res = db.name_resolution(file_id); + let must_use_params_expr = match &*db.module_kind(file_id) { + ModuleKind::Package { lambda_expr } + | ModuleKind::ConfigModule { lambda_expr } + | ModuleKind::Config { lambda_expr } => Some(*lambda_expr), + _ => None, + }; + // Unused let-bindings are eagerly collected into this. let mut unused_defs = Vec::new(); @@ -145,8 +153,26 @@ pub(crate) fn liveness_check_query( let mut unused_recs = Vec::new(); for (expr, kind) in module.exprs() { match kind { - &Expr::Lambda(Some(param), Some(_), _) if visited_defs.get(param).is_none() => { - unused_defs.push(param); + Expr::Lambda(param, pat, _) => { + // `{ ... }@bar: ...` + // ^ Unused and removable. + if let Some(param) = *param { + if pat.is_some() && visited_defs.get(param).is_none() { + unused_defs.push(param); + } + } + // `{ foo, ... }[@bar]: ...` + // ^ Unused and removable, only for packages and configurations. + if must_use_params_expr == Some(expr) { + if let Some(pat) = pat { + unused_defs.extend( + pat.fields + .iter() + .filter_map(|(name, _)| *name) + .filter(|name| visited_defs.get(*name).is_none()), + ); + } + } } &Expr::With(..) if visited_withs.get(expr).is_none() => { unused_withs.push(expr); @@ -251,4 +277,14 @@ mod tests { fn with_used_by_unused_let() { check("with 1; let $0a = from_with; in 1"); } + + #[test] + fn unused_pat_param_package() { + check("{ stdenv, $0hello }: stdenv.mkDerivation { }"); + } + + #[test] + fn unused_pat_param_config() { + check("{ $0lib, pkgs, ... }: { foo = with pkgs; [ bar ]; }"); + } } diff --git a/docs/features.md b/docs/features.md index cb67e85..3111f76 100644 --- a/docs/features.md +++ b/docs/features.md @@ -43,12 +43,13 @@ This incomplete list tracks noteble features currently implemented or planned. - [x] Diagnostics. `textDocument/publishDiagnostics` - - [x] Syntax errors. + - [x] Syntax errors. - [x] Hard semantic errors reported as parse errors by Nix, like duplicated keys in attrsets. - [x] Undefiend names. - [x] Warnings of legacy syntax. - [x] Warnings of unnecessary syntax. - [x] Warnings of unused bindings, `with` and `rec`. + - [x] Warnings of unused parameters for packages and modules. - [ ] Client pulled diagnostics. - [x] Custom filter on kinds. - [x] Exclude files.