use lsp_types::DiagnosticTag;
use nu_protocol::ast::{Expr, Expression, ExternalArgument};
use crate::{
config::LintLevel,
context::LintContext,
rule::{DetectFix, Rule},
violation::Detection,
};
struct AvoidNuSubprocess;
impl DetectFix for AvoidNuSubprocess {
type FixInput<'a> = ();
fn id(&self) -> &'static str {
"redundant_nu_subprocess"
}
fn short_description(&self) -> &'static str {
"Redundant `nu -c` subprocess call"
}
fn level(&self) -> LintLevel {
LintLevel::Error
}
fn diagnostic_tags(&self) -> &'static [DiagnosticTag] {
&[DiagnosticTag::UNNECESSARY]
}
fn detect<'a>(&self, context: &'a LintContext) -> Vec<(Detection, Self::FixInput<'a>)> {
Self::no_fix(context.detect(|expr, ctx| Self::check_expression(expr, ctx)))
}
}
impl AvoidNuSubprocess {
fn check_expression(expr: &Expression, ctx: &LintContext) -> Vec<Detection> {
let Expr::ExternalCall(head, args) = &expr.expr else {
return vec![];
};
let cmd_name = ctx.expr_text(head);
if !Self::is_nu_with_c_flag(cmd_name, args, ctx) {
return vec![];
}
vec![
Detection::from_global_span(
"Avoid spawning `nu -c` subprocess from within a Nu script",
expr.span,
)
.with_primary_label("subprocess spawned here"),
]
}
fn is_nu_with_c_flag(cmd_name: &str, args: &[ExternalArgument], ctx: &LintContext) -> bool {
if cmd_name != "nu" {
return false;
}
args.iter().any(|arg| {
let ExternalArgument::Regular(expr) = arg else {
return false;
};
let arg_text = ctx.expr_text(expr);
arg_text == "-c" || arg_text == "--commands"
})
}
}
pub static RULE: &dyn Rule = &AvoidNuSubprocess;
#[cfg(test)]
mod detect_bad;
#[cfg(test)]
mod ignore_good;